2009-12-11 19:24:15 +10:00
/*
* Copyright 2009 Red Hat Inc .
*
* 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 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 HOLDER ( S ) OR AUTHOR ( S ) 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 .
*
* Author : Ben Skeggs
*/
2013-01-31 09:04:48 +10:00
# include <core/object.h>
# include <core/class.h>
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/drm_crtc_helper.h>
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
# include "nouveau_drm.h"
# include "nouveau_reg.h"
2013-03-21 15:45:11 +10:00
# include "hw.h"
2009-12-11 19:24:15 +10:00
# include "nouveau_encoder.h"
# include "nouveau_connector.h"
2013-02-11 20:15:03 +10:00
# include <subdev/i2c.h>
2010-07-24 17:37:33 +02:00
int
nv04_display_early_init ( struct drm_device * dev )
{
2011-11-22 13:59:30 +10:00
/* ensure vblank interrupts are off, they can't be enabled until
* drm_vblank has been initialised
*/
NVWriteCRTC ( dev , 0 , NV_PCRTC_INTR_EN_0 , 0 ) ;
if ( nv_two_heads ( dev ) )
NVWriteCRTC ( dev , 1 , NV_PCRTC_INTR_EN_0 , 0 ) ;
2010-07-24 17:37:33 +02:00
return 0 ;
}
void
nv04_display_late_takedown ( struct drm_device * dev )
{
}
2009-12-11 19:24:15 +10:00
int
nv04_display_create ( struct drm_device * dev )
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2013-02-11 20:15:03 +10:00
struct nouveau_i2c * i2c = nouveau_i2c ( drm - > device ) ;
2012-07-31 16:16:21 +10:00
struct dcb_table * dcb = & drm - > vbios . dcb ;
2010-06-28 14:35:50 +10:00
struct drm_connector * connector , * ct ;
2009-12-11 19:24:15 +10:00
struct drm_encoder * encoder ;
struct drm_crtc * crtc ;
2012-07-18 10:00:50 +10:00
struct nv04_display * disp ;
2009-12-11 19:24:15 +10:00
int i , ret ;
2012-07-18 10:00:50 +10:00
disp = kzalloc ( sizeof ( * disp ) , GFP_KERNEL ) ;
if ( ! disp )
return - ENOMEM ;
2012-07-31 16:16:21 +10:00
nouveau_display ( dev ) - > priv = disp ;
nouveau_display ( dev ) - > dtor = nv04_display_destroy ;
nouveau_display ( dev ) - > init = nv04_display_init ;
nouveau_display ( dev ) - > fini = nv04_display_fini ;
2009-12-11 19:24:15 +10:00
2009-12-11 23:44:49 +01:00
nouveau_hw_save_vga_fonts ( dev , 1 ) ;
2009-12-11 19:24:15 +10:00
2013-01-31 09:04:48 +10:00
ret = nouveau_object_new ( nv_object ( drm ) , NVDRM_DEVICE , 0xd1500000 ,
NV04_DISP_CLASS , NULL , 0 , & disp - > core ) ;
if ( ret )
return ret ;
2009-12-11 19:24:15 +10:00
nv04_crtc_create ( dev , 0 ) ;
if ( nv_two_heads ( dev ) )
nv04_crtc_create ( dev , 1 ) ;
for ( i = 0 ; i < dcb - > entries ; i + + ) {
2012-07-11 10:44:20 +10:00
struct dcb_output * dcbent = & dcb - > entry [ i ] ;
2009-12-11 19:24:15 +10:00
2010-06-28 14:35:50 +10:00
connector = nouveau_connector_create ( dev , dcbent - > connector ) ;
if ( IS_ERR ( connector ) )
continue ;
2009-12-11 19:24:15 +10:00
switch ( dcbent - > type ) {
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_ANALOG :
2010-06-28 14:35:50 +10:00
ret = nv04_dac_create ( connector , dcbent ) ;
2009-12-11 19:24:15 +10:00
break ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_LVDS :
case DCB_OUTPUT_TMDS :
2010-06-28 14:35:50 +10:00
ret = nv04_dfp_create ( connector , dcbent ) ;
2009-12-11 19:24:15 +10:00
break ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_TV :
2009-12-11 19:24:15 +10:00
if ( dcbent - > location = = DCB_LOC_ON_CHIP )
2010-06-28 14:35:50 +10:00
ret = nv17_tv_create ( connector , dcbent ) ;
2009-12-11 19:24:15 +10:00
else
2010-06-28 14:35:50 +10:00
ret = nv04_tv_create ( connector , dcbent ) ;
2009-12-11 19:24:15 +10:00
break ;
default :
2012-07-31 16:16:21 +10:00
NV_WARN ( drm , " DCB type %d not known \n " , dcbent - > type ) ;
2009-12-11 19:24:15 +10:00
continue ;
}
if ( ret )
continue ;
}
2010-06-28 14:35:50 +10:00
list_for_each_entry_safe ( connector , ct ,
& dev - > mode_config . connector_list , head ) {
if ( ! connector - > encoder_ids [ 0 ] ) {
2012-07-31 16:16:21 +10:00
NV_WARN ( drm , " %s has no encoders, removing \n " ,
2010-06-28 14:35:50 +10:00
drm_get_connector_name ( connector ) ) ;
connector - > funcs - > destroy ( connector ) ;
}
}
2009-12-11 19:24:15 +10:00
2013-02-11 20:15:03 +10:00
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
struct nouveau_encoder * nv_encoder = nouveau_encoder ( encoder ) ;
nv_encoder - > i2c = i2c - > find ( i2c , nv_encoder - > dcb - > i2c_index ) ;
}
2009-12-11 19:24:15 +10:00
/* Save previous state */
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head )
crtc - > funcs - > save ( crtc ) ;
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
struct drm_encoder_helper_funcs * func = encoder - > helper_private ;
func - > save ( encoder ) ;
}
return 0 ;
}
void
nv04_display_destroy ( struct drm_device * dev )
{
2012-07-18 10:00:50 +10:00
struct nv04_display * disp = nv04_display ( dev ) ;
2009-12-11 19:24:15 +10:00
struct drm_encoder * encoder ;
struct drm_crtc * crtc ;
/* Turn every CRTC off. */
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head ) {
struct drm_mode_set modeset = {
. crtc = crtc ,
} ;
2012-12-11 13:47:23 +01:00
drm_mode_set_config_internal ( & modeset ) ;
2009-12-11 19:24:15 +10:00
}
/* Restore state */
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
struct drm_encoder_helper_funcs * func = encoder - > helper_private ;
func - > restore ( encoder ) ;
}
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head )
crtc - > funcs - > restore ( crtc ) ;
2009-12-11 23:44:49 +01:00
nouveau_hw_save_vga_fonts ( dev , 0 ) ;
2012-07-18 10:00:50 +10:00
2012-07-31 16:16:21 +10:00
nouveau_display ( dev ) - > priv = NULL ;
2012-07-18 10:00:50 +10:00
kfree ( disp ) ;
2009-12-11 19:24:15 +10:00
}
2010-07-24 17:37:33 +02:00
int
nv04_display_init ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
struct drm_encoder * encoder ;
struct drm_crtc * crtc ;
/* meh.. modeset apparently doesn't setup all the regs and depends
* on pre - existing state , for now load the state of the card * before *
* nouveau was loaded , and then do a modeset .
*
* best thing to do probably is to make save / restore routines not
* save / restore " pre-load " state , but more general so we can save
* on suspend too .
*/
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
struct drm_encoder_helper_funcs * func = encoder - > helper_private ;
func - > restore ( encoder ) ;
}
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head )
crtc - > funcs - > restore ( crtc ) ;
2010-07-24 17:37:33 +02:00
return 0 ;
2009-12-11 19:24:15 +10:00
}
2011-11-09 11:36:33 +10:00
void
nv04_display_fini ( struct drm_device * dev )
{
2011-11-22 13:59:30 +10:00
/* disable vblank interrupts */
NVWriteCRTC ( dev , 0 , NV_PCRTC_INTR_EN_0 , 0 ) ;
if ( nv_two_heads ( dev ) )
NVWriteCRTC ( dev , 1 , NV_PCRTC_INTR_EN_0 , 0 ) ;
2011-11-09 11:36:33 +10:00
}