2009-12-11 19:24:15 +10: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-07-31 16:16:21 +10:00
# include "nouveau_drm.h"
# include "nouveau_dma.h"
2009-12-11 19:24:15 +10:00
# include "nv50_display.h"
# include "nouveau_crtc.h"
# include "nouveau_encoder.h"
# include "nouveau_connector.h"
2010-03-30 05:34:18 +00:00
# include "nouveau_fbcon.h"
2012-10-02 18:01:07 +01:00
# include <drm/drm_crtc_helper.h>
2012-07-22 11:55:54 +10:00
# include "nouveau_fence.h"
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
# include <core/gpuobj.h>
2012-11-09 09:22:31 +10:00
# include <core/class.h>
2012-07-31 16:16:21 +10:00
# include <subdev/timer.h>
2009-12-11 19:24:15 +10:00
2011-02-03 20:06:14 +10:00
static void nv50_display_bh ( unsigned long ) ;
2010-11-03 10:27:27 +10:00
2010-09-06 11:39:25 +10:00
static inline int
nv50_sor_nr ( struct drm_device * dev )
{
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
2010-09-06 11:39:25 +10:00
2012-07-31 16:16:21 +10:00
if ( device - > chipset < 0x90 | |
device - > chipset = = 0x92 | |
device - > chipset = = 0xa0 )
2010-09-06 11:39:25 +10:00
return 2 ;
return 4 ;
}
2012-01-23 13:22:58 +10:00
u32
nv50_display_active_crtcs ( struct drm_device * dev )
{
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
2012-01-23 13:22:58 +10:00
u32 mask = 0 ;
int i ;
2012-07-31 16:16:21 +10:00
if ( device - > chipset < 0x90 | |
device - > chipset = = 0x92 | |
device - > chipset = = 0xa0 ) {
2012-01-23 13:22:58 +10:00
for ( i = 0 ; i < 2 ; i + + )
2012-07-31 16:16:21 +10:00
mask | = nv_rd32 ( device , NV50_PDISPLAY_SOR_MODE_CTRL_C ( i ) ) ;
2012-01-23 13:22:58 +10:00
} else {
for ( i = 0 ; i < 4 ; i + + )
2012-07-31 16:16:21 +10:00
mask | = nv_rd32 ( device , NV90_PDISPLAY_SOR_MODE_CTRL_C ( i ) ) ;
2012-01-23 13:22:58 +10:00
}
for ( i = 0 ; i < 3 ; i + + )
2012-07-31 16:16:21 +10:00
mask | = nv_rd32 ( device , NV50_PDISPLAY_DAC_MODE_CTRL_C ( i ) ) ;
2012-01-23 13:22:58 +10:00
return mask & 3 ;
}
2010-07-24 17:37:33 +02:00
int
nv50_display_early_init ( struct drm_device * dev )
{
return 0 ;
}
void
nv50_display_late_takedown ( struct drm_device * dev )
{
}
2011-10-14 14:35:19 +10:00
int
nv50_display_sync ( struct drm_device * dev )
{
struct nv50_display * disp = nv50_display ( dev ) ;
struct nouveau_channel * evo = disp - > master ;
int ret ;
ret = RING_SPACE ( evo , 6 ) ;
if ( ret = = 0 ) {
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0084 , 1 ) ;
2011-10-14 14:35:19 +10:00
OUT_RING ( evo , 0x80000000 ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0080 , 1 ) ;
2011-10-14 14:35:19 +10:00
OUT_RING ( evo , 0 ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0084 , 1 ) ;
2011-10-14 14:35:19 +10:00
OUT_RING ( evo , 0x00000000 ) ;
2012-07-20 08:17:34 +10:00
nv_wo32 ( disp - > ramin , 0x2000 , 0x00000000 ) ;
2011-10-14 14:35:19 +10:00
FIRE_RING ( evo ) ;
2012-07-31 16:16:21 +10:00
if ( nv_wait_ne ( disp - > ramin , 0x2000 , 0xffffffff , 0x00000000 ) )
return 0 ;
2011-10-14 14:35:19 +10:00
}
2012-07-20 08:17:34 +10:00
return 0 ;
2011-10-14 14:35:19 +10:00
}
2009-12-11 19:24:15 +10:00
int
nv50_display_init ( struct drm_device * dev )
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_device * device = nouveau_dev ( dev ) ;
2010-10-19 13:05:51 +10:00
struct nouveau_channel * evo ;
2009-12-11 19:24:15 +10:00
int ret , i ;
for ( i = 0 ; i < 3 ; i + + ) {
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_DAC_DPMS_CTRL ( i ) , 0x00550000 |
2009-12-11 19:24:15 +10:00
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING ) ;
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_DAC_CLK_CTRL1 ( i ) , 0x00000001 ) ;
2009-12-11 19:24:15 +10:00
}
2010-10-19 11:14:17 +10:00
for ( i = 0 ; i < 2 ; i + + ) {
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) , 0x2000 ) ;
if ( ! nv_wait ( device , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ,
2010-10-19 11:14:17 +10:00
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS , 0 ) ) {
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " timeout: CURSOR_CTRL2_STATUS == 0 \n " ) ;
NV_ERROR ( drm , " CURSOR_CTRL2 = 0x%08x \n " ,
nv_rd32 ( device , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ) ) ;
2010-10-19 11:14:17 +10:00
return - EBUSY ;
}
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ,
2010-10-19 11:14:17 +10:00
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON ) ;
2012-07-31 16:16:21 +10:00
if ( ! nv_wait ( device , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ,
2010-10-19 11:14:17 +10:00
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS ,
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE ) ) {
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " timeout: "
2010-10-19 11:14:17 +10:00
" CURSOR_CTRL2_STATUS_ACTIVE(%d) \n " , i ) ;
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " CURSOR_CTRL2(%d) = 0x%08x \n " , i ,
nv_rd32 ( device , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ) ) ;
2010-10-19 11:14:17 +10:00
return - EBUSY ;
}
}
2010-10-19 13:05:51 +10:00
ret = nv50_evo_init ( dev ) ;
2009-12-11 19:24:15 +10:00
if ( ret )
return ret ;
2011-02-01 10:24:41 +10:00
evo = nv50_display ( dev ) - > master ;
2009-12-11 19:24:15 +10:00
2011-10-14 16:13:10 +10:00
ret = RING_SPACE ( evo , 3 ) ;
2009-12-11 19:24:15 +10:00
if ( ret )
return ret ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , NV50_EVO_UNK84 , 2 ) ;
2011-10-14 16:13:10 +10:00
OUT_RING ( evo , NV50_EVO_UNK84_NOTIFY_DISABLED ) ;
OUT_RING ( evo , NvEvoSync ) ;
2009-12-11 19:24:15 +10:00
2011-10-14 16:13:10 +10:00
return nv50_display_sync ( dev ) ;
2009-12-11 19:24:15 +10:00
}
2011-11-09 11:36:33 +10:00
void
nv50_display_fini ( 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 ) ;
struct nouveau_device * device = nouveau_dev ( dev ) ;
2011-02-01 10:07:32 +10:00
struct nv50_display * disp = nv50_display ( dev ) ;
2011-02-01 10:24:41 +10:00
struct nouveau_channel * evo = disp - > master ;
2009-12-11 19:24:15 +10:00
struct drm_crtc * drm_crtc ;
int ret , i ;
list_for_each_entry ( drm_crtc , & dev - > mode_config . crtc_list , head ) {
struct nouveau_crtc * crtc = nouveau_crtc ( drm_crtc ) ;
nv50_crtc_blank ( crtc , true ) ;
}
2011-02-01 10:07:32 +10:00
ret = RING_SPACE ( evo , 2 ) ;
2009-12-11 19:24:15 +10:00
if ( ret = = 0 ) {
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , NV50_EVO_UPDATE , 1 ) ;
2011-02-01 10:07:32 +10:00
OUT_RING ( evo , 0 ) ;
2009-12-11 19:24:15 +10:00
}
2011-02-01 10:07:32 +10:00
FIRE_RING ( evo ) ;
2009-12-11 19:24:15 +10:00
/* Almost like ack'ing a vblank interrupt, maybe in the spirit of
* cleaning up ?
*/
list_for_each_entry ( drm_crtc , & dev - > mode_config . crtc_list , head ) {
struct nouveau_crtc * crtc = nouveau_crtc ( drm_crtc ) ;
uint32_t mask = NV50_PDISPLAY_INTR_1_VBLANK_CRTC_ ( crtc - > index ) ;
if ( ! crtc - > base . enabled )
continue ;
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_INTR_1 , mask ) ;
if ( ! nv_wait ( device , NV50_PDISPLAY_INTR_1 , mask , mask ) ) {
NV_ERROR ( drm , " timeout: (0x610024 & 0x%08x) == "
2009-12-11 19:24:15 +10:00
" 0x%08x \n " , mask , mask ) ;
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " 0x610024 = 0x%08x \n " ,
nv_rd32 ( device , NV50_PDISPLAY_INTR_1 ) ) ;
2009-12-11 19:24:15 +10:00
}
}
2011-07-04 10:47:19 +10:00
for ( i = 0 ; i < 2 ; i + + ) {
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) , 0 ) ;
if ( ! nv_wait ( device , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ,
2011-07-04 10:47:19 +10:00
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS , 0 ) ) {
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " timeout: CURSOR_CTRL2_STATUS == 0 \n " ) ;
NV_ERROR ( drm , " CURSOR_CTRL2 = 0x%08x \n " ,
nv_rd32 ( device , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ) ) ;
2011-07-04 10:47:19 +10:00
}
}
2010-10-19 13:05:51 +10:00
nv50_evo_fini ( dev ) ;
2009-12-11 19:24:15 +10:00
for ( i = 0 ; i < 3 ; i + + ) {
2012-07-31 16:16:21 +10:00
if ( ! nv_wait ( device , NV50_PDISPLAY_SOR_DPMS_STATE ( i ) ,
2009-12-11 19:24:15 +10:00
NV50_PDISPLAY_SOR_DPMS_STATE_WAIT , 0 ) ) {
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " timeout: SOR_DPMS_STATE_WAIT(%d) == 0 \n " , i ) ;
NV_ERROR ( drm , " SOR_DPMS_STATE(%d) = 0x%08x \n " , i ,
nv_rd32 ( device , NV50_PDISPLAY_SOR_DPMS_STATE ( i ) ) ) ;
2009-12-11 19:24:15 +10:00
}
}
}
2011-11-09 11:36:33 +10:00
int
nv50_display_create ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
2012-11-09 09:22:31 +10:00
static const u16 oclass [ ] = {
NVA3_DISP_CLASS ,
NV94_DISP_CLASS ,
NVA0_DISP_CLASS ,
NV84_DISP_CLASS ,
NV50_DISP_CLASS ,
} ;
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct dcb_table * dcb = & drm - > vbios . dcb ;
2010-06-28 14:35:50 +10:00
struct drm_connector * connector , * ct ;
2011-02-01 10:07:32 +10:00
struct nv50_display * priv ;
2011-11-09 15:52:43 +10:00
int ret , i ;
2009-12-11 19:24:15 +10:00
2011-02-01 10:07:32 +10:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2012-07-31 16:16:21 +10:00
nouveau_display ( dev ) - > priv = priv ;
nouveau_display ( dev ) - > dtor = nv50_display_destroy ;
nouveau_display ( dev ) - > init = nv50_display_init ;
nouveau_display ( dev ) - > fini = nv50_display_fini ;
2011-02-01 10:07:32 +10:00
2012-11-09 09:22:31 +10:00
/* attempt to allocate a supported evo display class */
ret = - ENODEV ;
for ( i = 0 ; ret & & i < ARRAY_SIZE ( oclass ) ; i + + ) {
ret = nouveau_object_new ( nv_object ( drm ) , NVDRM_DEVICE ,
0xd1500000 , oclass [ i ] , NULL , 0 ,
& priv - > core ) ;
}
if ( ret )
return ret ;
2009-12-11 19:24:15 +10:00
/* Create CRTC objects */
2012-04-20 11:01:46 +10:00
for ( i = 0 ; i < 2 ; i + + ) {
ret = nv50_crtc_create ( dev , i ) ;
if ( ret )
return ret ;
}
2009-12-11 19:24:15 +10:00
/* We setup the encoders from the BIOS table */
for ( i = 0 ; i < dcb - > entries ; i + + ) {
2012-07-11 10:44:20 +10:00
struct dcb_output * entry = & dcb - > entry [ i ] ;
2009-12-11 19:24:15 +10:00
if ( entry - > location ! = DCB_LOC_ON_CHIP ) {
2012-07-31 16:16:21 +10:00
NV_WARN ( drm , " Off-chip encoder %d/%d unsupported \n " ,
2009-12-11 19:24:15 +10:00
entry - > type , ffs ( entry - > or ) - 1 ) ;
continue ;
}
2010-06-28 14:35:50 +10:00
connector = nouveau_connector_create ( dev , entry - > connector ) ;
if ( IS_ERR ( connector ) )
continue ;
2009-12-11 19:24:15 +10:00
switch ( entry - > type ) {
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_TMDS :
case DCB_OUTPUT_LVDS :
case DCB_OUTPUT_DP :
2010-06-28 14:35:50 +10:00
nv50_sor_create ( connector , entry ) ;
2009-12-11 19:24:15 +10:00
break ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_ANALOG :
2010-06-28 14:35:50 +10:00
nv50_dac_create ( connector , entry ) ;
2009-12-11 19:24:15 +10:00
break ;
default :
2012-07-31 16:16:21 +10:00
NV_WARN ( drm , " DCB encoder %d unknown \n " , entry - > type ) ;
2009-12-11 19:24:15 +10:00
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
}
2011-02-03 20:06:14 +10:00
tasklet_init ( & priv - > tasklet , nv50_display_bh , ( unsigned long ) dev ) ;
2011-11-09 15:52:43 +10:00
ret = nv50_evo_create ( dev ) ;
if ( ret ) {
nv50_display_destroy ( dev ) ;
return ret ;
}
2009-12-11 19:24:15 +10:00
return 0 ;
}
2010-07-24 17:37:33 +02:00
void
nv50_display_destroy ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
2011-02-01 10:07:32 +10:00
struct nv50_display * disp = nv50_display ( dev ) ;
2011-01-26 17:49:18 +01:00
2011-11-09 15:52:43 +10:00
nv50_evo_destroy ( dev ) ;
2011-02-01 10:07:32 +10:00
kfree ( disp ) ;
2009-12-11 19:24:15 +10:00
}
2012-07-31 16:16:21 +10:00
struct nouveau_bo *
nv50_display_crtc_sema ( struct drm_device * dev , int crtc )
{
return nv50_display ( dev ) - > crtc [ crtc ] . sem . bo ;
}
2011-02-07 13:29:23 +10:00
void
nv50_display_flip_stop ( struct drm_crtc * crtc )
{
struct nv50_display * disp = nv50_display ( crtc - > dev ) ;
struct nouveau_crtc * nv_crtc = nouveau_crtc ( crtc ) ;
struct nv50_display_crtc * dispc = & disp - > crtc [ nv_crtc - > index ] ;
struct nouveau_channel * evo = dispc - > sync ;
int ret ;
ret = RING_SPACE ( evo , 8 ) ;
if ( ret ) {
WARN_ON ( 1 ) ;
return ;
}
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0084 , 1 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , 0x00000000 ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0094 , 1 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , 0x00000000 ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x00c0 , 1 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , 0x00000000 ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0080 , 1 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , 0x00000000 ) ;
FIRE_RING ( evo ) ;
}
int
nv50_display_flip_next ( struct drm_crtc * crtc , struct drm_framebuffer * fb ,
struct nouveau_channel * chan )
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( crtc - > dev ) ;
2011-02-07 13:29:23 +10:00
struct nouveau_framebuffer * nv_fb = nouveau_framebuffer ( fb ) ;
struct nv50_display * disp = nv50_display ( crtc - > dev ) ;
struct nouveau_crtc * nv_crtc = nouveau_crtc ( crtc ) ;
struct nv50_display_crtc * dispc = & disp - > crtc [ nv_crtc - > index ] ;
struct nouveau_channel * evo = dispc - > sync ;
int ret ;
2011-06-16 14:40:27 +10:00
ret = RING_SPACE ( evo , chan ? 25 : 27 ) ;
2011-02-07 13:29:23 +10:00
if ( unlikely ( ret ) )
return ret ;
/* synchronise with the rendering channel, if necessary */
if ( likely ( chan ) ) {
ret = RING_SPACE ( chan , 10 ) ;
if ( ret ) {
WIND_RING ( evo ) ;
return ret ;
}
2012-07-31 16:16:21 +10:00
if ( nv_device ( drm - > device ) - > chipset < 0xc0 ) {
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( chan , 0 , 0x0060 , 2 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( chan , NvEvoSema0 + nv_crtc - > index ) ;
OUT_RING ( chan , dispc - > sem . offset ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( chan , 0 , 0x006c , 1 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( chan , 0xf00d0000 | dispc - > sem . value ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( chan , 0 , 0x0064 , 2 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( chan , dispc - > sem . offset ^ 0x10 ) ;
OUT_RING ( chan , 0x74b1e000 ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( chan , 0 , 0x0060 , 1 ) ;
2012-07-31 16:16:21 +10:00
if ( nv_device ( drm - > device ) - > chipset < 0x84 )
2011-02-07 13:29:23 +10:00
OUT_RING ( chan , NvSema ) ;
else
2012-07-20 08:17:34 +10:00
OUT_RING ( chan , chan - > vram ) ;
2011-02-07 13:29:23 +10:00
} else {
2012-07-22 11:55:54 +10:00
u64 offset = nvc0_fence_crtc ( chan , nv_crtc - > index ) ;
2011-06-07 15:43:31 +10:00
offset + = dispc - > sem . offset ;
2012-04-01 21:09:13 +10:00
BEGIN_NVC0 ( chan , 0 , 0x0010 , 4 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( chan , upper_32_bits ( offset ) ) ;
OUT_RING ( chan , lower_32_bits ( offset ) ) ;
OUT_RING ( chan , 0xf00d0000 | dispc - > sem . value ) ;
OUT_RING ( chan , 0x1002 ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NVC0 ( chan , 0 , 0x0010 , 4 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( chan , upper_32_bits ( offset ) ) ;
OUT_RING ( chan , lower_32_bits ( offset ^ 0x10 ) ) ;
OUT_RING ( chan , 0x74b1e000 ) ;
OUT_RING ( chan , 0x1001 ) ;
}
FIRE_RING ( chan ) ;
} else {
nouveau_bo_wr32 ( dispc - > sem . bo , dispc - > sem . offset / 4 ,
0xf00d0000 | dispc - > sem . value ) ;
}
/* queue the flip on the crtc's "display sync" channel */
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0100 , 1 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , 0xfffe0000 ) ;
2011-06-16 14:40:27 +10:00
if ( chan ) {
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0084 , 1 ) ;
2011-06-16 14:40:27 +10:00
OUT_RING ( evo , 0x00000100 ) ;
} else {
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0084 , 1 ) ;
2011-06-16 14:40:27 +10:00
OUT_RING ( evo , 0x00000010 ) ;
/* allows gamma somehow, PDISP will bitch at you if
* you don ' t wait for vblank before changing this . .
*/
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x00e0 , 1 ) ;
2011-06-16 14:40:27 +10:00
OUT_RING ( evo , 0x40000000 ) ;
}
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0088 , 4 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , dispc - > sem . offset ) ;
OUT_RING ( evo , 0xf00d0000 | dispc - > sem . value ) ;
OUT_RING ( evo , 0x74b1e000 ) ;
OUT_RING ( evo , NvEvoSync ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x00a0 , 2 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , 0x00000000 ) ;
OUT_RING ( evo , 0x00000000 ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x00c0 , 1 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , nv_fb - > r_dma ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0110 , 2 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , 0x00000000 ) ;
OUT_RING ( evo , 0x00000000 ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0800 , 5 ) ;
2011-06-07 11:24:14 +10:00
OUT_RING ( evo , nv_fb - > nvbo - > bo . offset > > 8 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , 0 ) ;
OUT_RING ( evo , ( fb - > height < < 16 ) | fb - > width ) ;
OUT_RING ( evo , nv_fb - > r_pitch ) ;
OUT_RING ( evo , nv_fb - > r_format ) ;
2012-04-01 21:09:13 +10:00
BEGIN_NV04 ( evo , 0 , 0x0080 , 1 ) ;
2011-02-07 13:29:23 +10:00
OUT_RING ( evo , 0x00000000 ) ;
FIRE_RING ( evo ) ;
dispc - > sem . offset ^ = 0x10 ;
dispc - > sem . value + + ;
return 0 ;
}
2010-07-06 08:54:34 +10:00
static u16
2012-07-11 10:44:20 +10:00
nv50_display_script_select ( struct drm_device * dev , struct dcb_output * dcb ,
2010-07-06 08:54:34 +10:00
u32 mc , int pxclk )
2009-12-11 19:24:15 +10:00
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2009-12-21 12:16:52 +10:00
struct nouveau_connector * nv_connector = NULL ;
struct drm_encoder * encoder ;
2012-07-31 16:16:21 +10:00
struct nvbios * bios = & drm - > vbios ;
2010-07-06 08:54:34 +10:00
u32 script = 0 , or ;
2009-12-11 19:24:15 +10:00
2009-12-21 12:16:52 +10:00
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
struct nouveau_encoder * nv_encoder = nouveau_encoder ( encoder ) ;
2010-07-06 08:54:34 +10:00
if ( nv_encoder - > dcb ! = dcb )
2009-12-21 12:16:52 +10:00
continue ;
nv_connector = nouveau_encoder_connector_get ( nv_encoder ) ;
break ;
}
2010-07-06 08:54:34 +10:00
or = ffs ( dcb - > or ) - 1 ;
switch ( dcb - > type ) {
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_LVDS :
2009-12-11 19:24:15 +10:00
script = ( mc > > 8 ) & 0xf ;
2010-02-24 10:03:05 +10:00
if ( bios - > fp_no_ddc ) {
2009-12-11 19:24:15 +10:00
if ( bios - > fp . dual_link )
script | = 0x0100 ;
if ( bios - > fp . if_is_24bit )
script | = 0x0200 ;
} else {
2011-04-18 10:49:03 +10:00
/* determine number of lvds links */
if ( nv_connector & & nv_connector - > edid & &
2011-11-18 10:23:59 +10:00
nv_connector - > type = = DCB_CONNECTOR_LVDS_SPWG ) {
2011-04-18 10:49:03 +10:00
/* http://www.spwg.org */
if ( ( ( u8 * ) nv_connector - > edid ) [ 121 ] = = 2 )
script | = 0x0100 ;
} else
2009-12-11 19:24:15 +10:00
if ( pxclk > = bios - > fp . duallink_transition_clk ) {
script | = 0x0100 ;
2011-04-18 10:49:03 +10:00
}
/* determine panel depth */
if ( script & 0x0100 ) {
2009-12-11 19:24:15 +10:00
if ( bios - > fp . strapless_is_24bit & 2 )
script | = 0x0200 ;
2011-04-18 10:49:03 +10:00
} else {
if ( bios - > fp . strapless_is_24bit & 1 )
script | = 0x0200 ;
}
2009-12-21 12:16:52 +10:00
if ( nv_connector & & nv_connector - > edid & &
( nv_connector - > edid - > revision > = 4 ) & &
( nv_connector - > edid - > input & 0x70 ) > = 0x20 )
script | = 0x0200 ;
2009-12-11 19:24:15 +10:00
}
break ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_TMDS :
2009-12-11 19:24:15 +10:00
script = ( mc > > 8 ) & 0xf ;
if ( pxclk > = 165000 )
script | = 0x0100 ;
break ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_DP :
2009-12-11 19:24:15 +10:00
script = ( mc > > 8 ) & 0xf ;
break ;
2012-07-11 10:44:20 +10:00
case DCB_OUTPUT_ANALOG :
2009-12-11 19:24:15 +10:00
script = 0xff ;
break ;
default :
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " modeset on unsupported output type! \n " ) ;
2009-12-11 19:24:15 +10:00
break ;
}
return script ;
}
static void
nv50_display_unk10_handler ( struct drm_device * dev )
{
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2011-02-01 10:07:32 +10:00
struct nv50_display * disp = nv50_display ( dev ) ;
2012-07-31 16:16:21 +10:00
u32 unk30 = nv_rd32 ( device , 0x610030 ) , mc ;
2012-07-11 10:44:20 +10:00
int i , crtc , or = 0 , type = DCB_OUTPUT_ANY ;
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " 0x610030: 0x%08x \n " , unk30 ) ;
2011-02-01 10:07:32 +10:00
disp - > irq . dcb = NULL ;
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , 0x619494 , nv_rd32 ( device , 0x619494 ) & ~ 8 ) ;
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
/* Determine which CRTC we're dealing with, only 1 ever will be
* signalled at the same time with the current nouveau code .
*/
crtc = ffs ( ( unk30 & 0x00000060 ) > > 5 ) - 1 ;
if ( crtc < 0 )
goto ack ;
/* Nothing needs to be done for the encoder */
crtc = ffs ( ( unk30 & 0x00000180 ) > > 7 ) - 1 ;
if ( crtc < 0 )
goto ack ;
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
/* Find which encoder was connected to the CRTC */
2012-07-11 10:44:20 +10:00
for ( i = 0 ; type = = DCB_OUTPUT_ANY & & i < 3 ; i + + ) {
2012-07-31 16:16:21 +10:00
mc = nv_rd32 ( device , NV50_PDISPLAY_DAC_MODE_CTRL_C ( i ) ) ;
NV_DEBUG ( drm , " DAC-%d mc: 0x%08x \n " , i , mc ) ;
2010-07-06 08:54:34 +10:00
if ( ! ( mc & ( 1 < < crtc ) ) )
continue ;
switch ( ( mc & 0x00000f00 ) > > 8 ) {
2012-07-11 10:44:20 +10:00
case 0 : type = DCB_OUTPUT_ANALOG ; break ;
case 1 : type = DCB_OUTPUT_TV ; break ;
2010-07-06 08:54:34 +10:00
default :
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " invalid mc, DAC-%d: 0x%08x \n " , i , mc ) ;
2010-07-06 08:54:34 +10:00
goto ack ;
}
or = i ;
}
2012-07-11 10:44:20 +10:00
for ( i = 0 ; type = = DCB_OUTPUT_ANY & & i < nv50_sor_nr ( dev ) ; i + + ) {
2012-07-31 16:16:21 +10:00
if ( nv_device ( drm - > device ) - > chipset < 0x90 | |
nv_device ( drm - > device ) - > chipset = = 0x92 | |
nv_device ( drm - > device ) - > chipset = = 0xa0 )
mc = nv_rd32 ( device , NV50_PDISPLAY_SOR_MODE_CTRL_C ( i ) ) ;
2010-07-06 08:54:34 +10:00
else
2012-07-31 16:16:21 +10:00
mc = nv_rd32 ( device , NV90_PDISPLAY_SOR_MODE_CTRL_C ( i ) ) ;
2010-07-06 08:54:34 +10:00
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " SOR-%d mc: 0x%08x \n " , i , mc ) ;
2010-07-06 08:54:34 +10:00
if ( ! ( mc & ( 1 < < crtc ) ) )
continue ;
switch ( ( mc & 0x00000f00 ) > > 8 ) {
2012-07-11 10:44:20 +10:00
case 0 : type = DCB_OUTPUT_LVDS ; break ;
case 1 : type = DCB_OUTPUT_TMDS ; break ;
case 2 : type = DCB_OUTPUT_TMDS ; break ;
case 5 : type = DCB_OUTPUT_TMDS ; break ;
case 8 : type = DCB_OUTPUT_DP ; break ;
case 9 : type = DCB_OUTPUT_DP ; break ;
2010-07-06 08:54:34 +10:00
default :
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " invalid mc, SOR-%d: 0x%08x \n " , i , mc ) ;
2010-07-06 08:54:34 +10:00
goto ack ;
}
or = i ;
}
/* There was no encoder to disable */
2012-07-11 10:44:20 +10:00
if ( type = = DCB_OUTPUT_ANY )
2010-07-06 08:54:34 +10:00
goto ack ;
/* Disable the encoder */
2012-07-31 16:16:21 +10:00
for ( i = 0 ; i < drm - > vbios . dcb . entries ; i + + ) {
struct dcb_output * dcb = & drm - > vbios . dcb . entry [ i ] ;
2010-07-06 08:54:34 +10:00
if ( dcb - > type = = type & & ( dcb - > or & ( 1 < < or ) ) ) {
2011-07-06 21:21:42 +10:00
nouveau_bios_run_display_table ( dev , 0 , - 1 , dcb , - 1 ) ;
2011-02-01 10:07:32 +10:00
disp - > irq . dcb = dcb ;
2010-07-06 08:54:34 +10:00
goto ack ;
}
}
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " no dcb for %d %d 0x%08x \n " , or , type , mc ) ;
2009-12-11 19:24:15 +10:00
ack :
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_INTR_1 , NV50_PDISPLAY_INTR_1_CLK_UNK10 ) ;
nv_wr32 ( device , 0x610030 , 0x80000000 ) ;
2009-12-11 19:24:15 +10:00
}
static void
nv50_display_unk20_handler ( struct drm_device * dev )
{
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2011-02-01 10:07:32 +10:00
struct nv50_display * disp = nv50_display ( dev ) ;
2012-07-31 16:16:21 +10:00
u32 unk30 = nv_rd32 ( device , 0x610030 ) , tmp , pclk , script , mc = 0 ;
2012-07-11 10:44:20 +10:00
struct dcb_output * dcb ;
int i , crtc , or = 0 , type = DCB_OUTPUT_ANY ;
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " 0x610030: 0x%08x \n " , unk30 ) ;
2011-02-01 10:07:32 +10:00
dcb = disp - > irq . dcb ;
2010-07-06 08:54:34 +10:00
if ( dcb ) {
2011-07-06 21:21:42 +10:00
nouveau_bios_run_display_table ( dev , 0 , - 2 , dcb , - 1 ) ;
2011-02-01 10:07:32 +10:00
disp - > irq . dcb = NULL ;
2010-07-06 08:54:34 +10:00
}
/* CRTC clock change requested? */
crtc = ffs ( ( unk30 & 0x00000600 ) > > 9 ) - 1 ;
if ( crtc > = 0 ) {
2012-07-31 16:16:21 +10:00
pclk = nv_rd32 ( device , NV50_PDISPLAY_CRTC_P ( crtc , CLOCK ) ) ;
2010-07-06 08:54:34 +10:00
pclk & = 0x003fffff ;
2011-10-14 16:13:10 +10:00
if ( pclk )
nv50_crtc_set_clock ( dev , crtc , pclk ) ;
2010-07-06 08:54:34 +10:00
2012-07-31 16:16:21 +10:00
tmp = nv_rd32 ( device , NV50_PDISPLAY_CRTC_CLK_CTRL2 ( crtc ) ) ;
2010-07-06 08:54:34 +10:00
tmp & = ~ 0x000000f ;
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_CRTC_CLK_CTRL2 ( crtc ) , tmp ) ;
2010-07-06 08:54:34 +10:00
}
/* Nothing needs to be done for the encoder */
crtc = ffs ( ( unk30 & 0x00000180 ) > > 7 ) - 1 ;
if ( crtc < 0 )
2009-12-11 19:24:15 +10:00
goto ack ;
2012-07-31 16:16:21 +10:00
pclk = nv_rd32 ( device , NV50_PDISPLAY_CRTC_P ( crtc , CLOCK ) ) & 0x003fffff ;
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
/* Find which encoder is connected to the CRTC */
2012-07-11 10:44:20 +10:00
for ( i = 0 ; type = = DCB_OUTPUT_ANY & & i < 3 ; i + + ) {
2012-07-31 16:16:21 +10:00
mc = nv_rd32 ( device , NV50_PDISPLAY_DAC_MODE_CTRL_P ( i ) ) ;
NV_DEBUG ( drm , " DAC-%d mc: 0x%08x \n " , i , mc ) ;
2010-07-06 08:54:34 +10:00
if ( ! ( mc & ( 1 < < crtc ) ) )
continue ;
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
switch ( ( mc & 0x00000f00 ) > > 8 ) {
2012-07-11 10:44:20 +10:00
case 0 : type = DCB_OUTPUT_ANALOG ; break ;
case 1 : type = DCB_OUTPUT_TV ; break ;
2010-07-06 08:54:34 +10:00
default :
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " invalid mc, DAC-%d: 0x%08x \n " , i , mc ) ;
2010-07-06 08:54:34 +10:00
goto ack ;
}
or = i ;
}
2012-07-11 10:44:20 +10:00
for ( i = 0 ; type = = DCB_OUTPUT_ANY & & i < nv50_sor_nr ( dev ) ; i + + ) {
2012-07-31 16:16:21 +10:00
if ( nv_device ( drm - > device ) - > chipset < 0x90 | |
nv_device ( drm - > device ) - > chipset = = 0x92 | |
nv_device ( drm - > device ) - > chipset = = 0xa0 )
mc = nv_rd32 ( device , NV50_PDISPLAY_SOR_MODE_CTRL_P ( i ) ) ;
2010-07-06 08:54:34 +10:00
else
2012-07-31 16:16:21 +10:00
mc = nv_rd32 ( device , NV90_PDISPLAY_SOR_MODE_CTRL_P ( i ) ) ;
2010-07-06 08:54:34 +10:00
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " SOR-%d mc: 0x%08x \n " , i , mc ) ;
2010-07-06 08:54:34 +10:00
if ( ! ( mc & ( 1 < < crtc ) ) )
continue ;
switch ( ( mc & 0x00000f00 ) > > 8 ) {
2012-07-11 10:44:20 +10:00
case 0 : type = DCB_OUTPUT_LVDS ; break ;
case 1 : type = DCB_OUTPUT_TMDS ; break ;
case 2 : type = DCB_OUTPUT_TMDS ; break ;
case 5 : type = DCB_OUTPUT_TMDS ; break ;
case 8 : type = DCB_OUTPUT_DP ; break ;
case 9 : type = DCB_OUTPUT_DP ; break ;
2010-07-06 08:54:34 +10:00
default :
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " invalid mc, SOR-%d: 0x%08x \n " , i , mc ) ;
2010-07-06 08:54:34 +10:00
goto ack ;
}
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
or = i ;
}
2009-12-11 19:24:15 +10:00
2012-07-11 10:44:20 +10:00
if ( type = = DCB_OUTPUT_ANY )
2010-07-06 08:54:34 +10:00
goto ack ;
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
/* Enable the encoder */
2012-07-31 16:16:21 +10:00
for ( i = 0 ; i < drm - > vbios . dcb . entries ; i + + ) {
dcb = & drm - > vbios . dcb . entry [ i ] ;
2010-07-06 08:54:34 +10:00
if ( dcb - > type = = type & & ( dcb - > or & ( 1 < < or ) ) )
break ;
}
2010-04-23 08:21:48 +10:00
2012-07-31 16:16:21 +10:00
if ( i = = drm - > vbios . dcb . entries ) {
NV_ERROR ( drm , " no dcb for %d %d 0x%08x \n " , or , type , mc ) ;
2010-07-06 08:54:34 +10:00
goto ack ;
}
script = nv50_display_script_select ( dev , dcb , mc , pclk ) ;
2011-07-06 21:21:42 +10:00
nouveau_bios_run_display_table ( dev , script , pclk , dcb , - 1 ) ;
2009-12-11 19:24:15 +10:00
2012-07-11 10:44:20 +10:00
if ( type = = DCB_OUTPUT_DP ) {
2011-07-01 15:51:49 +10:00
int link = ! ( dcb - > dpconf . sor . link & 1 ) ;
if ( ( mc & 0x000f0000 ) = = 0x00020000 )
2012-03-09 16:22:56 +10:00
nv50_sor_dp_calc_tu ( dev , or , link , pclk , 18 ) ;
2011-07-01 15:51:49 +10:00
else
2012-03-09 16:22:56 +10:00
nv50_sor_dp_calc_tu ( dev , or , link , pclk , 24 ) ;
2011-07-01 15:51:49 +10:00
}
2010-07-06 08:54:34 +10:00
2012-07-11 10:44:20 +10:00
if ( dcb - > type ! = DCB_OUTPUT_ANALOG ) {
2012-07-31 16:16:21 +10:00
tmp = nv_rd32 ( device , NV50_PDISPLAY_SOR_CLK_CTRL2 ( or ) ) ;
2009-12-11 19:24:15 +10:00
tmp & = ~ 0x00000f0f ;
if ( script & 0x0100 )
tmp | = 0x00000101 ;
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_SOR_CLK_CTRL2 ( or ) , tmp ) ;
2009-12-11 19:24:15 +10:00
} else {
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_DAC_CLK_CTRL2 ( or ) , 0 ) ;
2009-12-11 19:24:15 +10:00
}
2011-02-01 10:07:32 +10:00
disp - > irq . dcb = dcb ;
disp - > irq . pclk = pclk ;
disp - > irq . script = script ;
2010-07-06 08:54:34 +10:00
2009-12-11 19:24:15 +10:00
ack :
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_INTR_1 , NV50_PDISPLAY_INTR_1_CLK_UNK20 ) ;
nv_wr32 ( device , 0x610030 , 0x80000000 ) ;
2009-12-11 19:24:15 +10:00
}
2010-07-09 10:37:42 +10:00
/* If programming a TMDS output on a SOR that can also be configured for
* DisplayPort , make sure NV50_SOR_DP_CTRL_ENABLE is forced off .
*
* It looks like the VBIOS TMDS scripts make an attempt at this , however ,
* the VBIOS scripts on at least one board I have only switch it off on
* link 0 , causing a blank display if the output has previously been
* programmed for DisplayPort .
*/
static void
2012-07-11 10:44:20 +10:00
nv50_display_unk40_dp_set_tmds ( struct drm_device * dev , struct dcb_output * dcb )
2010-07-09 10:37:42 +10:00
{
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
2010-07-09 10:37:42 +10:00
int or = ffs ( dcb - > or ) - 1 , link = ! ( dcb - > dpconf . sor . link & 1 ) ;
struct drm_encoder * encoder ;
u32 tmp ;
2012-07-11 10:44:20 +10:00
if ( dcb - > type ! = DCB_OUTPUT_TMDS )
2010-07-09 10:37:42 +10:00
return ;
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
struct nouveau_encoder * nv_encoder = nouveau_encoder ( encoder ) ;
2012-07-11 10:44:20 +10:00
if ( nv_encoder - > dcb - > type = = DCB_OUTPUT_DP & &
2010-07-09 10:37:42 +10:00
nv_encoder - > dcb - > or & ( 1 < < or ) ) {
2012-07-31 16:16:21 +10:00
tmp = nv_rd32 ( device , NV50_SOR_DP_CTRL ( or , link ) ) ;
2010-07-09 10:37:42 +10:00
tmp & = ~ NV50_SOR_DP_CTRL_ENABLED ;
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_SOR_DP_CTRL ( or , link ) , tmp ) ;
2010-07-09 10:37:42 +10:00
break ;
}
}
}
2009-12-11 19:24:15 +10:00
static void
nv50_display_unk40_handler ( struct drm_device * dev )
{
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2011-02-01 10:07:32 +10:00
struct nv50_display * disp = nv50_display ( dev ) ;
2012-07-11 10:44:20 +10:00
struct dcb_output * dcb = disp - > irq . dcb ;
2011-02-01 10:07:32 +10:00
u16 script = disp - > irq . script ;
2012-07-31 16:16:21 +10:00
u32 unk30 = nv_rd32 ( device , 0x610030 ) , pclk = disp - > irq . pclk ;
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " 0x610030: 0x%08x \n " , unk30 ) ;
2011-02-01 10:07:32 +10:00
disp - > irq . dcb = NULL ;
2010-07-06 08:54:34 +10:00
if ( ! dcb )
2009-12-11 19:24:15 +10:00
goto ack ;
2011-07-06 21:21:42 +10:00
nouveau_bios_run_display_table ( dev , script , - pclk , dcb , - 1 ) ;
2010-07-09 10:37:42 +10:00
nv50_display_unk40_dp_set_tmds ( dev , dcb ) ;
2009-12-11 19:24:15 +10:00
ack :
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_INTR_1 , NV50_PDISPLAY_INTR_1_CLK_UNK40 ) ;
nv_wr32 ( device , 0x610030 , 0x80000000 ) ;
nv_wr32 ( device , 0x619494 , nv_rd32 ( device , 0x619494 ) | 8 ) ;
2009-12-11 19:24:15 +10:00
}
2011-02-03 20:06:14 +10:00
static void
nv50_display_bh ( unsigned long data )
2009-12-11 19:24:15 +10:00
{
2011-02-03 20:06:14 +10:00
struct drm_device * dev = ( struct drm_device * ) data ;
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2009-12-11 19:24:15 +10:00
for ( ; ; ) {
2012-07-31 16:16:21 +10:00
uint32_t intr0 = nv_rd32 ( device , NV50_PDISPLAY_INTR_0 ) ;
uint32_t intr1 = nv_rd32 ( device , NV50_PDISPLAY_INTR_1 ) ;
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " PDISPLAY_INTR_BH 0x%08x 0x%08x \n " , intr0 , intr1 ) ;
2009-12-11 19:24:15 +10:00
if ( intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10 )
nv50_display_unk10_handler ( dev ) ;
else
if ( intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20 )
nv50_display_unk20_handler ( dev ) ;
else
if ( intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40 )
nv50_display_unk40_handler ( dev ) ;
else
break ;
}
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV03_PMC_INTR_EN_0 , 1 ) ;
2009-12-11 19:24:15 +10:00
}
static void
nv50_display_error_handler ( struct drm_device * dev )
{
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
u32 channels = ( nv_rd32 ( device , NV50_PDISPLAY_INTR_0 ) & 0x001f0000 ) > > 16 ;
2010-10-20 14:23:29 +10:00
u32 addr , data ;
int chid ;
2009-12-11 19:24:15 +10:00
2010-10-20 14:23:29 +10:00
for ( chid = 0 ; chid < 5 ; chid + + ) {
if ( ! ( channels & ( 1 < < chid ) ) )
continue ;
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_INTR_0 , 0x00010000 < < chid ) ;
addr = nv_rd32 ( device , NV50_PDISPLAY_TRAPPED_ADDR ( chid ) ) ;
data = nv_rd32 ( device , NV50_PDISPLAY_TRAPPED_DATA ( chid ) ) ;
NV_ERROR ( drm , " EvoCh %d Mthd 0x%04x Data 0x%08x "
2010-10-20 14:23:29 +10:00
" (0x%04x 0x%02x) \n " , chid ,
addr & 0xffc , data , addr > > 16 , ( addr > > 12 ) & 0xf ) ;
2009-12-11 19:24:15 +10:00
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_TRAPPED_ADDR ( chid ) , 0x90000000 ) ;
2010-10-20 14:23:29 +10:00
}
2009-12-11 19:24:15 +10:00
}
2012-07-31 16:16:21 +10:00
void
nv50_display_intr ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2011-02-03 20:06:14 +10:00
struct nv50_display * disp = nv50_display ( dev ) ;
2009-12-11 19:24:15 +10:00
uint32_t delayed = 0 ;
2012-07-31 16:16:21 +10:00
while ( nv_rd32 ( device , NV50_PMC_INTR_0 ) & NV50_PMC_INTR_0_DISPLAY ) {
uint32_t intr0 = nv_rd32 ( device , NV50_PDISPLAY_INTR_0 ) ;
uint32_t intr1 = nv_rd32 ( device , NV50_PDISPLAY_INTR_1 ) ;
2009-12-11 19:24:15 +10:00
uint32_t clock ;
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " PDISPLAY_INTR 0x%08x 0x%08x \n " , intr0 , intr1 ) ;
2009-12-11 19:24:15 +10:00
if ( ! intr0 & & ! ( intr1 & ~ delayed ) )
break ;
2010-10-20 14:23:29 +10:00
if ( intr0 & 0x001f0000 ) {
2009-12-11 19:24:15 +10:00
nv50_display_error_handler ( dev ) ;
2010-10-20 14:23:29 +10:00
intr0 & = ~ 0x001f0000 ;
2009-12-11 19:24:15 +10:00
}
if ( intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC ) {
intr1 & = ~ NV50_PDISPLAY_INTR_1_VBLANK_CRTC ;
2012-07-20 08:17:34 +10:00
delayed | = NV50_PDISPLAY_INTR_1_VBLANK_CRTC ;
2009-12-11 19:24:15 +10:00
}
clock = ( intr1 & ( NV50_PDISPLAY_INTR_1_CLK_UNK10 |
NV50_PDISPLAY_INTR_1_CLK_UNK20 |
NV50_PDISPLAY_INTR_1_CLK_UNK40 ) ) ;
if ( clock ) {
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV03_PMC_INTR_EN_0 , 0 ) ;
2011-02-03 20:06:14 +10:00
tasklet_schedule ( & disp - > tasklet ) ;
2009-12-11 19:24:15 +10:00
delayed | = clock ;
intr1 & = ~ clock ;
}
if ( intr0 ) {
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm , " unknown PDISPLAY_INTR_0: 0x%08x \n " , intr0 ) ;
nv_wr32 ( device , NV50_PDISPLAY_INTR_0 , intr0 ) ;
2009-12-11 19:24:15 +10:00
}
if ( intr1 ) {
2012-07-31 16:16:21 +10:00
NV_ERROR ( drm ,
2009-12-11 19:24:15 +10:00
" unknown PDISPLAY_INTR_1: 0x%08x \n " , intr1 ) ;
2012-07-31 16:16:21 +10:00
nv_wr32 ( device , NV50_PDISPLAY_INTR_1 , intr1 ) ;
2009-12-11 19:24:15 +10:00
}
}
}