2009-12-11 19:24:15 +10:00
/*
* Copyright © 2007 David Airlie
*
* 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 AUTHORS OR COPYRIGHT HOLDERS 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 .
*
* Authors :
* David Airlie
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/tty.h>
# include <linux/sysrq.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/screen_info.h>
2010-02-01 15:38:10 +10:00
# include <linux/vga_switcheroo.h>
2011-11-09 14:31:16 +10:00
# include <linux/console.h>
2009-12-11 19:24:15 +10:00
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/drm_crtc.h>
# include <drm/drm_crtc_helper.h>
# include <drm/drm_fb_helper.h>
2016-12-22 00:50:42 -08:00
# include <drm/drm_atomic.h>
2012-07-20 08:17:34 +10:00
2016-05-20 09:22:55 +10:00
# include "nouveau_drv.h"
2012-07-20 08:17:34 +10:00
# include "nouveau_gem.h"
# include "nouveau_bo.h"
2009-12-11 19:24:15 +10:00
# include "nouveau_fbcon.h"
2012-07-20 08:17:34 +10:00
# include "nouveau_chan.h"
2017-11-01 03:56:19 +10:00
# include "nouveau_vmm.h"
2012-07-20 08:17:34 +10:00
# include "nouveau_crtc.h"
MODULE_PARM_DESC ( nofbaccel , " Disable fbcon acceleration " ) ;
2014-08-18 22:43:24 +02:00
int nouveau_nofbaccel = 0 ;
2012-07-20 08:17:34 +10:00
module_param_named ( nofbaccel , nouveau_nofbaccel , int , 0400 ) ;
2009-12-11 19:24:15 +10:00
2010-10-05 16:41:29 +10:00
static void
nouveau_fbcon_fillrect ( struct fb_info * info , const struct fb_fillrect * rect )
{
2012-07-20 08:17:34 +10:00
struct nouveau_fbdev * fbcon = info - > par ;
2016-11-04 17:20:35 +10:00
struct nouveau_drm * drm = nouveau_drm ( fbcon - > helper . dev ) ;
2016-05-18 13:57:42 +10:00
struct nvif_device * device = & drm - > client . device ;
2010-10-05 16:41:29 +10:00
int ret ;
if ( info - > state ! = FBINFO_STATE_RUNNING )
return ;
ret = - ENODEV ;
2010-10-14 14:55:23 +10:00
if ( ! in_interrupt ( ) & & ! ( info - > flags & FBINFO_HWACCEL_DISABLED ) & &
2012-07-20 08:17:34 +10:00
mutex_trylock ( & drm - > client . mutex ) ) {
2014-08-10 04:10:22 +10:00
if ( device - > info . family < NV_DEVICE_INFO_V0_TESLA )
2010-10-05 16:41:29 +10:00
ret = nv04_fbcon_fillrect ( info , rect ) ;
else
2014-08-10 04:10:22 +10:00
if ( device - > info . family < NV_DEVICE_INFO_V0_FERMI )
2010-10-05 16:41:29 +10:00
ret = nv50_fbcon_fillrect ( info , rect ) ;
2010-11-24 10:52:43 +10:00
else
ret = nvc0_fbcon_fillrect ( info , rect ) ;
2012-07-20 08:17:34 +10:00
mutex_unlock ( & drm - > client . mutex ) ;
2010-10-05 16:41:29 +10:00
}
if ( ret = = 0 )
return ;
if ( ret ! = - ENODEV )
nouveau_fbcon_gpu_lockup ( info ) ;
2015-07-31 16:21:57 +05:30
drm_fb_helper_cfb_fillrect ( info , rect ) ;
2010-10-05 16:41:29 +10:00
}
static void
nouveau_fbcon_copyarea ( struct fb_info * info , const struct fb_copyarea * image )
{
2012-07-20 08:17:34 +10:00
struct nouveau_fbdev * fbcon = info - > par ;
2016-11-04 17:20:35 +10:00
struct nouveau_drm * drm = nouveau_drm ( fbcon - > helper . dev ) ;
2016-05-18 13:57:42 +10:00
struct nvif_device * device = & drm - > client . device ;
2010-10-05 16:41:29 +10:00
int ret ;
if ( info - > state ! = FBINFO_STATE_RUNNING )
return ;
ret = - ENODEV ;
2010-10-14 14:55:23 +10:00
if ( ! in_interrupt ( ) & & ! ( info - > flags & FBINFO_HWACCEL_DISABLED ) & &
2012-07-20 08:17:34 +10:00
mutex_trylock ( & drm - > client . mutex ) ) {
2014-08-10 04:10:22 +10:00
if ( device - > info . family < NV_DEVICE_INFO_V0_TESLA )
2010-10-05 16:41:29 +10:00
ret = nv04_fbcon_copyarea ( info , image ) ;
else
2014-08-10 04:10:22 +10:00
if ( device - > info . family < NV_DEVICE_INFO_V0_FERMI )
2010-10-05 16:41:29 +10:00
ret = nv50_fbcon_copyarea ( info , image ) ;
2010-11-24 10:52:43 +10:00
else
ret = nvc0_fbcon_copyarea ( info , image ) ;
2012-07-20 08:17:34 +10:00
mutex_unlock ( & drm - > client . mutex ) ;
2010-10-05 16:41:29 +10:00
}
if ( ret = = 0 )
return ;
if ( ret ! = - ENODEV )
nouveau_fbcon_gpu_lockup ( info ) ;
2015-07-31 16:21:57 +05:30
drm_fb_helper_cfb_copyarea ( info , image ) ;
2010-10-05 16:41:29 +10:00
}
static void
nouveau_fbcon_imageblit ( struct fb_info * info , const struct fb_image * image )
{
2012-07-20 08:17:34 +10:00
struct nouveau_fbdev * fbcon = info - > par ;
2016-11-04 17:20:35 +10:00
struct nouveau_drm * drm = nouveau_drm ( fbcon - > helper . dev ) ;
2016-05-18 13:57:42 +10:00
struct nvif_device * device = & drm - > client . device ;
2010-10-05 16:41:29 +10:00
int ret ;
if ( info - > state ! = FBINFO_STATE_RUNNING )
return ;
ret = - ENODEV ;
2010-10-14 14:55:23 +10:00
if ( ! in_interrupt ( ) & & ! ( info - > flags & FBINFO_HWACCEL_DISABLED ) & &
2012-07-20 08:17:34 +10:00
mutex_trylock ( & drm - > client . mutex ) ) {
2014-08-10 04:10:22 +10:00
if ( device - > info . family < NV_DEVICE_INFO_V0_TESLA )
2010-10-05 16:41:29 +10:00
ret = nv04_fbcon_imageblit ( info , image ) ;
else
2014-08-10 04:10:22 +10:00
if ( device - > info . family < NV_DEVICE_INFO_V0_FERMI )
2010-10-05 16:41:29 +10:00
ret = nv50_fbcon_imageblit ( info , image ) ;
2010-11-24 10:52:43 +10:00
else
ret = nvc0_fbcon_imageblit ( info , image ) ;
2012-07-20 08:17:34 +10:00
mutex_unlock ( & drm - > client . mutex ) ;
2010-10-05 16:41:29 +10:00
}
if ( ret = = 0 )
return ;
if ( ret ! = - ENODEV )
nouveau_fbcon_gpu_lockup ( info ) ;
2015-07-31 16:21:57 +05:30
drm_fb_helper_cfb_imageblit ( info , image ) ;
2010-10-05 16:41:29 +10:00
}
2009-12-11 19:24:15 +10:00
static int
nouveau_fbcon_sync ( struct fb_info * info )
{
2012-07-20 08:17:34 +10:00
struct nouveau_fbdev * fbcon = info - > par ;
2016-11-04 17:20:35 +10:00
struct nouveau_drm * drm = nouveau_drm ( fbcon - > helper . dev ) ;
2012-07-20 08:17:34 +10:00
struct nouveau_channel * chan = drm - > channel ;
2012-05-04 14:01:28 +10:00
int ret ;
2009-12-11 19:24:15 +10:00
2010-10-05 16:53:48 +10:00
if ( ! chan | | ! chan - > accel_done | | in_interrupt ( ) | |
2009-12-11 19:24:15 +10:00
info - > state ! = FBINFO_STATE_RUNNING | |
info - > flags & FBINFO_HWACCEL_DISABLED )
return 0 ;
2012-07-20 08:17:34 +10:00
if ( ! mutex_trylock ( & drm - > client . mutex ) )
2010-10-14 14:55:23 +10:00
return 0 ;
2012-05-04 14:01:28 +10:00
ret = nouveau_channel_idle ( chan ) ;
2012-07-20 08:17:34 +10:00
mutex_unlock ( & drm - > client . mutex ) ;
2009-12-11 19:24:15 +10:00
if ( ret ) {
2010-01-04 19:25:09 +01:00
nouveau_fbcon_gpu_lockup ( info ) ;
2009-12-11 19:24:15 +10:00
return 0 ;
}
chan - > accel_done = false ;
return 0 ;
}
2015-10-02 14:03:19 +10:00
static int
nouveau_fbcon_open ( struct fb_info * info , int user )
{
struct nouveau_fbdev * fbcon = info - > par ;
2016-11-04 17:20:35 +10:00
struct nouveau_drm * drm = nouveau_drm ( fbcon - > helper . dev ) ;
2015-10-02 14:03:19 +10:00
int ret = pm_runtime_get_sync ( drm - > dev - > dev ) ;
if ( ret < 0 & & ret ! = - EACCES )
return ret ;
return 0 ;
}
static int
nouveau_fbcon_release ( struct fb_info * info , int user )
{
struct nouveau_fbdev * fbcon = info - > par ;
2016-11-04 17:20:35 +10:00
struct nouveau_drm * drm = nouveau_drm ( fbcon - > helper . dev ) ;
2015-10-02 14:03:19 +10:00
pm_runtime_put ( drm - > dev - > dev ) ;
return 0 ;
}
2009-12-11 19:24:15 +10:00
static struct fb_ops nouveau_fbcon_ops = {
. owner = THIS_MODULE ,
2016-11-14 00:03:18 +01:00
DRM_FB_HELPER_DEFAULT_OPS ,
2015-10-02 14:03:19 +10:00
. fb_open = nouveau_fbcon_open ,
. fb_release = nouveau_fbcon_release ,
2010-10-05 16:41:29 +10:00
. fb_fillrect = nouveau_fbcon_fillrect ,
. fb_copyarea = nouveau_fbcon_copyarea ,
. fb_imageblit = nouveau_fbcon_imageblit ,
2009-12-11 19:24:15 +10:00
. fb_sync = nouveau_fbcon_sync ,
} ;
2010-10-05 16:41:29 +10:00
static struct fb_ops nouveau_fbcon_sw_ops = {
2010-01-27 14:03:18 +00:00
. owner = THIS_MODULE ,
2016-11-14 00:03:18 +01:00
DRM_FB_HELPER_DEFAULT_OPS ,
2015-10-02 14:03:19 +10:00
. fb_open = nouveau_fbcon_open ,
. fb_release = nouveau_fbcon_release ,
2015-07-31 16:21:57 +05:30
. fb_fillrect = drm_fb_helper_cfb_fillrect ,
. fb_copyarea = drm_fb_helper_cfb_copyarea ,
. fb_imageblit = drm_fb_helper_cfb_imageblit ,
2010-01-27 14:03:18 +00:00
} ;
2014-06-28 20:44:07 +10:00
void
nouveau_fbcon_accel_save_disable ( struct drm_device * dev )
{
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2017-09-23 13:10:33 -07:00
if ( drm - > fbcon & & drm - > fbcon - > helper . fbdev ) {
2014-06-28 20:44:07 +10:00
drm - > fbcon - > saved_flags = drm - > fbcon - > helper . fbdev - > flags ;
drm - > fbcon - > helper . fbdev - > flags | = FBINFO_HWACCEL_DISABLED ;
}
}
void
nouveau_fbcon_accel_restore ( struct drm_device * dev )
{
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2017-09-23 13:10:33 -07:00
if ( drm - > fbcon & & drm - > fbcon - > helper . fbdev ) {
2014-06-28 20:44:07 +10:00
drm - > fbcon - > helper . fbdev - > flags = drm - > fbcon - > saved_flags ;
}
}
2014-08-10 12:36:26 +10:00
static void
2014-06-28 20:44:07 +10:00
nouveau_fbcon_accel_fini ( struct drm_device * dev )
{
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_fbdev * fbcon = drm - > fbcon ;
if ( fbcon & & drm - > channel ) {
console_lock ( ) ;
2017-09-23 13:10:33 -07:00
if ( fbcon - > helper . fbdev )
fbcon - > helper . fbdev - > flags | = FBINFO_HWACCEL_DISABLED ;
2014-06-28 20:44:07 +10:00
console_unlock ( ) ;
nouveau_channel_idle ( drm - > channel ) ;
2014-08-10 04:10:22 +10:00
nvif_object_fini ( & fbcon - > twod ) ;
nvif_object_fini ( & fbcon - > blit ) ;
nvif_object_fini ( & fbcon - > gdi ) ;
nvif_object_fini ( & fbcon - > patt ) ;
nvif_object_fini ( & fbcon - > rop ) ;
nvif_object_fini ( & fbcon - > clip ) ;
nvif_object_fini ( & fbcon - > surf2d ) ;
2014-06-28 20:44:07 +10:00
}
}
2014-08-10 12:36:26 +10:00
static void
2014-06-28 20:44:07 +10:00
nouveau_fbcon_accel_init ( struct drm_device * dev )
{
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_fbdev * fbcon = drm - > fbcon ;
struct fb_info * info = fbcon - > helper . fbdev ;
int ret ;
2016-05-18 13:57:42 +10:00
if ( drm - > client . device . info . family < NV_DEVICE_INFO_V0_TESLA )
2014-06-28 20:44:07 +10:00
ret = nv04_fbcon_accel_init ( info ) ;
else
2016-05-18 13:57:42 +10:00
if ( drm - > client . device . info . family < NV_DEVICE_INFO_V0_FERMI )
2014-06-28 20:44:07 +10:00
ret = nv50_fbcon_accel_init ( info ) ;
else
ret = nvc0_fbcon_accel_init ( info ) ;
if ( ret = = 0 )
info - > fbops = & nouveau_fbcon_ops ;
}
2010-03-30 05:34:13 +00:00
static void
2012-07-20 08:17:34 +10:00
nouveau_fbcon_zfill ( struct drm_device * dev , struct nouveau_fbdev * fbcon )
2009-12-11 19:24:15 +10:00
{
2012-07-20 08:17:34 +10:00
struct fb_info * info = fbcon - > helper . fbdev ;
2009-12-11 19:24:15 +10:00
struct fb_fillrect rect ;
/* Clear the entire fbcon. The drm will program every connector
* with it ' s preferred mode . If the sizes differ , one display will
* quite likely have garbage around the console .
*/
rect . dx = rect . dy = 0 ;
rect . width = info - > var . xres_virtual ;
rect . height = info - > var . yres_virtual ;
rect . color = 0 ;
rect . rop = ROP_COPY ;
info - > fbops - > fb_fillrect ( info , & rect ) ;
}
static int
2013-01-21 23:42:49 +01:00
nouveau_fbcon_create ( struct drm_fb_helper * helper ,
2010-03-30 05:34:14 +00:00
struct drm_fb_helper_surface_size * sizes )
2009-12-11 19:24:15 +10:00
{
2014-09-14 18:40:17 +02:00
struct nouveau_fbdev * fbcon =
container_of ( helper , struct nouveau_fbdev , helper ) ;
2016-11-04 17:20:35 +10:00
struct drm_device * dev = fbcon - > helper . dev ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2016-05-18 13:57:42 +10:00
struct nvif_device * device = & drm - > client . device ;
2009-12-11 19:24:15 +10:00
struct fb_info * info ;
2016-11-04 17:20:35 +10:00
struct nouveau_framebuffer * fb ;
2011-06-07 13:12:44 +10:00
struct nouveau_channel * chan ;
2009-12-11 19:24:15 +10:00
struct nouveau_bo * nvbo ;
2011-11-14 14:51:28 -08:00
struct drm_mode_fb_cmd2 mode_cmd ;
2016-11-04 17:20:35 +10:00
int ret ;
2009-12-11 19:24:15 +10:00
2010-03-30 05:34:13 +00:00
mode_cmd . width = sizes - > surface_width ;
mode_cmd . height = sizes - > surface_height ;
2009-12-11 19:24:15 +10:00
2011-11-14 14:51:28 -08:00
mode_cmd . pitches [ 0 ] = mode_cmd . width * ( sizes - > surface_bpp > > 3 ) ;
mode_cmd . pitches [ 0 ] = roundup ( mode_cmd . pitches [ 0 ] , 256 ) ;
2009-12-11 19:24:15 +10:00
2011-11-14 14:51:28 -08:00
mode_cmd . pixel_format = drm_mode_legacy_fb_format ( sizes - > surface_bpp ,
sizes - > surface_depth ) ;
2016-05-24 17:29:55 +10:00
ret = nouveau_gem_new ( & drm - > client , mode_cmd . pitches [ 0 ] *
mode_cmd . height , 0 , NOUVEAU_GEM_DOMAIN_VRAM ,
0 , 0x0000 , & nvbo ) ;
2009-12-11 19:24:15 +10:00
if ( ret ) {
2012-07-20 08:17:34 +10:00
NV_ERROR ( drm , " failed to allocate framebuffer \n " ) ;
2009-12-11 19:24:15 +10:00
goto out ;
}
2016-11-04 17:20:35 +10:00
ret = nouveau_framebuffer_new ( dev , & mode_cmd , nvbo , & fb ) ;
if ( ret )
goto out_unref ;
2016-11-04 17:20:35 +10:00
2014-11-10 11:24:27 +10:00
ret = nouveau_bo_pin ( nvbo , TTM_PL_FLAG_VRAM , false ) ;
2009-12-11 19:24:15 +10:00
if ( ret ) {
2012-07-20 08:17:34 +10:00
NV_ERROR ( drm , " failed to pin fb: %d \n " , ret ) ;
2013-06-27 13:38:21 +02:00
goto out_unref ;
2009-12-11 19:24:15 +10:00
}
ret = nouveau_bo_map ( nvbo ) ;
if ( ret ) {
2012-07-20 08:17:34 +10:00
NV_ERROR ( drm , " failed to map fb: %d \n " , ret ) ;
2013-06-27 13:38:21 +02:00
goto out_unpin ;
2009-12-11 19:24:15 +10:00
}
2012-07-20 08:17:34 +10:00
chan = nouveau_nofbaccel ? NULL : drm - > channel ;
2014-08-10 04:10:22 +10:00
if ( chan & & device - > info . family > = NV_DEVICE_INFO_V0_TESLA ) {
2017-11-01 03:56:19 +10:00
ret = nouveau_vma_new ( nvbo , & drm - > client . vmm , & fb - > vma ) ;
2011-06-07 13:12:44 +10:00
if ( ret ) {
2012-07-20 08:17:34 +10:00
NV_ERROR ( drm , " failed to map fb into chan: %d \n " , ret ) ;
2011-06-07 13:12:44 +10:00
chan = NULL ;
}
}
2015-07-31 16:21:57 +05:30
info = drm_fb_helper_alloc_fbi ( helper ) ;
if ( IS_ERR ( info ) ) {
ret = PTR_ERR ( info ) ;
2013-06-27 13:38:21 +02:00
goto out_unlock ;
2009-12-11 19:24:15 +10:00
}
2015-01-13 09:18:49 +01:00
info - > skip_vt_switch = 1 ;
2009-12-11 19:24:15 +10:00
2012-07-20 08:17:34 +10:00
info - > par = fbcon ;
2010-03-30 05:34:13 +00:00
2010-03-30 05:34:14 +00:00
/* setup helper */
2016-11-04 17:20:35 +10:00
fbcon - > helper . fb = & fb - > base ;
2009-12-11 19:24:15 +10:00
strcpy ( info - > fix . id , " nouveaufb " ) ;
2013-06-27 13:38:21 +02:00
if ( ! chan )
2010-01-26 14:00:42 +00:00
info - > flags = FBINFO_DEFAULT | FBINFO_HWACCEL_DISABLED ;
else
info - > flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
FBINFO_HWACCEL_FILLRECT |
FBINFO_HWACCEL_IMAGEBLIT ;
2010-06-23 12:56:12 -07:00
info - > flags | = FBINFO_CAN_FORCE_OUTPUT ;
2010-10-05 16:41:29 +10:00
info - > fbops = & nouveau_fbcon_sw_ops ;
2016-11-04 17:20:35 +10:00
info - > fix . smem_start = fb - > nvbo - > bo . mem . bus . base +
fb - > nvbo - > bo . mem . bus . offset ;
info - > fix . smem_len = fb - > nvbo - > bo . mem . num_pages < < PAGE_SHIFT ;
2009-12-11 19:24:15 +10:00
2016-11-04 17:20:35 +10:00
info - > screen_base = nvbo_kmap_obj_iovirtual ( fb - > nvbo ) ;
info - > screen_size = fb - > nvbo - > bo . mem . num_pages < < PAGE_SHIFT ;
2009-12-11 19:24:15 +10:00
2016-12-14 23:31:35 +02:00
drm_fb_helper_fill_fix ( info , fb - > base . pitches [ 0 ] ,
fb - > base . format - > depth ) ;
2012-07-20 08:17:34 +10:00
drm_fb_helper_fill_var ( info , & fbcon - > helper , sizes - > fb_width , sizes - > fb_height ) ;
2010-05-16 17:27:03 +02:00
2012-02-06 10:58:19 +01:00
/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
2009-12-11 19:24:15 +10:00
2014-06-28 20:44:07 +10:00
if ( chan )
nouveau_fbcon_accel_init ( dev ) ;
2012-07-20 08:17:34 +10:00
nouveau_fbcon_zfill ( dev , fbcon ) ;
2009-12-11 19:24:15 +10:00
/* To allow resizeing without swapping buffers */
2015-03-04 00:18:38 -05:00
NV_INFO ( drm , " allocated %dx%d fb: 0x%llx, bo %p \n " ,
2016-11-04 17:20:35 +10:00
fb - > base . width , fb - > base . height , fb - > nvbo - > bo . offset , nvbo ) ;
2009-12-11 19:24:15 +10:00
2010-02-01 15:38:10 +10:00
vga_switcheroo_client_fb_set ( dev - > pdev , info ) ;
2009-12-11 19:24:15 +10:00
return 0 ;
2013-06-27 13:38:21 +02:00
out_unlock :
if ( chan )
2017-11-01 03:56:19 +10:00
nouveau_vma_del ( & fb - > vma ) ;
2016-11-04 17:20:35 +10:00
nouveau_bo_unmap ( fb - > nvbo ) ;
2013-06-27 13:38:21 +02:00
out_unpin :
2016-11-04 17:20:35 +10:00
nouveau_bo_unpin ( fb - > nvbo ) ;
2013-06-27 13:38:21 +02:00
out_unref :
2016-11-04 17:20:35 +10:00
nouveau_bo_ref ( NULL , & fb - > nvbo ) ;
2009-12-11 19:24:15 +10:00
out :
return ret ;
}
2010-05-07 06:42:51 +00:00
void
nouveau_fbcon_output_poll_changed ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2013-07-23 15:45:11 +02:00
if ( drm - > fbcon )
drm_fb_helper_hotplug_event ( & drm - > fbcon - > helper ) ;
2009-12-11 19:24:15 +10:00
}
2010-07-03 18:36:39 +02:00
static int
2012-07-20 08:17:34 +10:00
nouveau_fbcon_destroy ( struct drm_device * dev , struct nouveau_fbdev * fbcon )
2009-12-11 19:24:15 +10:00
{
2016-11-04 17:20:35 +10:00
struct nouveau_framebuffer * nouveau_fb = nouveau_framebuffer ( fbcon - > helper . fb ) ;
2009-12-11 19:24:15 +10:00
2015-07-31 16:21:57 +05:30
drm_fb_helper_unregister_fbi ( & fbcon - > helper ) ;
2016-11-04 17:20:35 +10:00
drm_fb_helper_fini ( & fbcon - > helper ) ;
2009-12-11 19:24:15 +10:00
2010-03-30 05:34:14 +00:00
if ( nouveau_fb - > nvbo ) {
2017-11-01 03:56:19 +10:00
nouveau_vma_del ( & nouveau_fb - > vma ) ;
2016-11-04 17:20:35 +10:00
nouveau_bo_unmap ( nouveau_fb - > nvbo ) ;
2013-06-27 13:38:21 +02:00
nouveau_bo_unpin ( nouveau_fb - > nvbo ) ;
2016-11-04 17:20:35 +10:00
drm_framebuffer_unreference ( & nouveau_fb - > base ) ;
2009-12-11 19:24:15 +10:00
}
2016-11-04 17:20:35 +10:00
2009-12-11 19:24:15 +10:00
return 0 ;
}
2010-01-04 19:25:09 +01:00
void nouveau_fbcon_gpu_lockup ( struct fb_info * info )
{
2012-07-20 08:17:34 +10:00
struct nouveau_fbdev * fbcon = info - > par ;
2016-11-04 17:20:35 +10:00
struct nouveau_drm * drm = nouveau_drm ( fbcon - > helper . dev ) ;
2010-01-04 19:25:09 +01:00
2012-07-20 08:17:34 +10:00
NV_ERROR ( drm , " GPU lockup - switching to software fbcon \n " ) ;
2010-01-04 19:25:09 +01:00
info - > flags | = FBINFO_HWACCEL_DISABLED ;
}
2010-03-30 05:34:13 +00:00
2014-06-27 17:19:23 +02:00
static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
2013-01-21 23:42:49 +01:00
. fb_probe = nouveau_fbcon_create ,
2010-03-30 05:34:18 +00:00
} ;
2017-01-11 21:25:24 -05:00
static void
nouveau_fbcon_set_suspend_work ( struct work_struct * work )
{
struct nouveau_drm * drm = container_of ( work , typeof ( * drm ) , fbcon_work ) ;
int state = READ_ONCE ( drm - > fbcon_new_state ) ;
if ( state = = FBINFO_STATE_RUNNING )
pm_runtime_get_sync ( drm - > dev - > dev ) ;
console_lock ( ) ;
if ( state = = FBINFO_STATE_RUNNING )
nouveau_fbcon_accel_restore ( drm - > dev ) ;
drm_fb_helper_set_suspend ( & drm - > fbcon - > helper , state ) ;
if ( state ! = FBINFO_STATE_RUNNING )
nouveau_fbcon_accel_save_disable ( drm - > dev ) ;
console_unlock ( ) ;
if ( state = = FBINFO_STATE_RUNNING ) {
pm_runtime_mark_last_busy ( drm - > dev - > dev ) ;
pm_runtime_put_sync ( drm - > dev - > dev ) ;
}
}
2014-10-02 13:31:00 +10:00
void
nouveau_fbcon_set_suspend ( struct drm_device * dev , int state )
{
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2017-01-11 21:25:24 -05:00
if ( ! drm - > fbcon )
return ;
drm - > fbcon_new_state = state ;
/* Since runtime resume can happen as a result of a sysfs operation,
* it ' s possible we already have the console locked . So handle fbcon
* init / deinit from a seperate work thread
*/
schedule_work ( & drm - > fbcon_work ) ;
2014-10-02 13:31:00 +10:00
}
2012-07-20 08:17:34 +10:00
int
nouveau_fbcon_init ( struct drm_device * dev )
2010-03-30 05:34:13 +00:00
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2012-07-20 08:17:34 +10:00
struct nouveau_fbdev * fbcon ;
2011-11-06 20:32:04 +01:00
int preferred_bpp ;
2010-06-06 10:50:03 +01:00
int ret ;
2010-03-30 05:34:14 +00:00
2013-09-10 13:20:34 +10:00
if ( ! dev - > mode_config . num_crtc | |
( dev - > pdev - > class > > 8 ) ! = PCI_CLASS_DISPLAY_VGA )
2012-07-20 08:17:34 +10:00
return 0 ;
fbcon = kzalloc ( sizeof ( struct nouveau_fbdev ) , GFP_KERNEL ) ;
if ( ! fbcon )
2010-03-30 05:34:14 +00:00
return - ENOMEM ;
2012-07-20 08:17:34 +10:00
drm - > fbcon = fbcon ;
2017-01-11 21:25:24 -05:00
INIT_WORK ( & drm - > fbcon_work , nouveau_fbcon_set_suspend_work ) ;
2014-06-27 17:19:24 +02:00
drm_fb_helper_prepare ( dev , & fbcon - > helper , & nouveau_fbcon_helper_funcs ) ;
2010-03-30 05:34:14 +00:00
drm: Rely on mode_config data for fb_helper initialization
Instead of receiving the num_crts as a parameter, we can read it
directly from the mode_config structure. I audited the drivers that
invoke this helper and I believe all of them initialize the mode_config
struct accordingly, prior to calling the fb_helper.
I used the following coccinelle hack to make this transformation, except
for the function headers and comment updates. The first and second
rules are split because I couldn't find a way to remove the unused
temporary variables at the same time I removed the parameter.
// <smpl>
@r@
expression A,B,D,E;
identifier C;
@@
(
- drm_fb_helper_init(A,B,C,D)
+ drm_fb_helper_init(A,B,D)
|
- drm_fbdev_cma_init_with_funcs(A,B,C,D,E)
+ drm_fbdev_cma_init_with_funcs(A,B,D,E)
|
- drm_fbdev_cma_init(A,B,C,D)
+ drm_fbdev_cma_init(A,B,D)
)
@@
expression A,B,C,D,E;
@@
(
- drm_fb_helper_init(A,B,C,D)
+ drm_fb_helper_init(A,B,D)
|
- drm_fbdev_cma_init_with_funcs(A,B,C,D,E)
+ drm_fbdev_cma_init_with_funcs(A,B,D,E)
|
- drm_fbdev_cma_init(A,B,C,D)
+ drm_fbdev_cma_init(A,B,D)
)
@@
identifier r.C;
type T;
expression V;
@@
- T C;
<...
when != C
- C = V;
...>
// </smpl>
Changes since v1:
- Rebased on top of the tip of drm-misc-next.
- Remove mention to sti since a proper fix got merged.
Suggested-by: Daniel Vetter <daniel.vetter@intel.com>
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
Reviewed-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20170202162640.27261-1-krisman@collabora.co.uk
2017-02-02 14:26:40 -02:00
ret = drm_fb_helper_init ( dev , & fbcon - > helper , 4 ) ;
2014-12-19 11:21:32 +01:00
if ( ret )
goto free ;
2010-06-06 10:50:03 +01:00
2014-12-19 11:21:32 +01:00
ret = drm_fb_helper_single_add_all_connectors ( & fbcon - > helper ) ;
if ( ret )
goto fini ;
2011-11-06 20:32:04 +01:00
2016-05-18 13:57:42 +10:00
if ( drm - > client . device . info . ram_size < = 32 * 1024 * 1024 )
2011-11-06 20:32:04 +01:00
preferred_bpp = 8 ;
2012-07-20 08:17:34 +10:00
else
2016-05-18 13:57:42 +10:00
if ( drm - > client . device . info . ram_size < = 64 * 1024 * 1024 )
2011-11-06 20:32:04 +01:00
preferred_bpp = 16 ;
else
preferred_bpp = 32 ;
2013-01-20 23:12:54 +01:00
/* disable all the possible outputs/crtcs before entering KMS mode */
2016-12-22 00:50:42 -08:00
if ( ! drm_drv_uses_atomic_modeset ( dev ) )
2016-11-04 17:20:36 +10:00
drm_helper_disable_unused_functions ( dev ) ;
2013-01-20 23:12:54 +01:00
2014-12-19 11:21:32 +01:00
ret = drm_fb_helper_initial_config ( & fbcon - > helper , preferred_bpp ) ;
if ( ret )
goto fini ;
2016-06-20 13:52:14 +03:00
if ( fbcon - > helper . fbdev )
fbcon - > helper . fbdev - > pixmap . buf_align = 4 ;
2010-03-30 05:34:13 +00:00
return 0 ;
2014-12-19 11:21:32 +01:00
fini :
drm_fb_helper_fini ( & fbcon - > helper ) ;
free :
kfree ( fbcon ) ;
return ret ;
2010-03-30 05:34:13 +00:00
}
2012-07-20 08:17:34 +10:00
void
nouveau_fbcon_fini ( struct drm_device * dev )
2010-03-30 05:34:13 +00:00
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2010-03-30 05:34:14 +00:00
2012-07-20 08:17:34 +10:00
if ( ! drm - > fbcon )
2010-03-30 05:34:14 +00:00
return ;
2014-06-28 20:44:07 +10:00
nouveau_fbcon_accel_fini ( dev ) ;
2012-07-20 08:17:34 +10:00
nouveau_fbcon_destroy ( dev , drm - > fbcon ) ;
kfree ( drm - > fbcon ) ;
drm - > fbcon = NULL ;
2010-03-30 05:34:13 +00:00
}