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 .
*
*/
2011-02-03 16:07:44 +10:00
# define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
2009-12-11 19:24:15 +10:00
# include "nv50_display.h"
# include "nouveau_crtc.h"
# include "nouveau_encoder.h"
# include "nouveau_connector.h"
# include "nouveau_fb.h"
2010-03-30 05:34:18 +00:00
# include "nouveau_fbcon.h"
2010-09-01 15:24:31 +10:00
# include "nouveau_ramht.h"
2009-12-11 19:24:15 +10:00
# include "drm_crtc_helper.h"
2010-11-03 10:27:27 +10:00
static void nv50_display_isr ( struct drm_device * ) ;
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 )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
if ( dev_priv - > chipset < 0x90 | |
dev_priv - > chipset = = 0x92 | |
dev_priv - > chipset = = 0xa0 )
return 2 ;
return 4 ;
}
2011-10-14 16:19:42 +10:00
static int
evo_icmd ( struct drm_device * dev , int ch , u32 mthd , u32 data )
{
int ret = 0 ;
nv_mask ( dev , 0x610300 + ( ch * 0x08 ) , 0x00000001 , 0x00000001 ) ;
nv_wr32 ( dev , 0x610304 + ( ch * 0x08 ) , data ) ;
nv_wr32 ( dev , 0x610300 + ( ch * 0x08 ) , 0x80000001 | mthd ) ;
if ( ! nv_wait ( dev , 0x610300 + ( ch * 0x08 ) , 0x80000000 , 0x00000000 ) )
ret = - EBUSY ;
2011-10-29 11:31:29 +10:00
if ( ret | | ( nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO ) )
NV_INFO ( dev , " EvoPIO: %d 0x%04x 0x%08x \n " , ch , mthd , data ) ;
2011-10-14 16:19:42 +10:00
nv_mask ( dev , 0x610300 + ( ch * 0x08 ) , 0x00000001 , 0x00000000 ) ;
return ret ;
}
2010-07-24 17:37:33 +02:00
int
nv50_display_early_init ( struct drm_device * dev )
{
2011-10-29 11:31:29 +10:00
u32 ctrl = nv_rd32 ( dev , 0x610200 ) ;
2011-10-14 16:19:42 +10:00
int i ;
2011-10-29 11:31:29 +10:00
2011-10-14 16:19:42 +10:00
/* check if master evo channel is already active, a good a sign as any
* that the display engine is in a weird state ( hibernate / kexec ) , if
* it is , do our best to reset the display engine . . .
*/
2011-10-29 11:31:29 +10:00
if ( ( ctrl & 0x00000003 ) = = 0x00000003 ) {
NV_INFO ( dev , " PDISP: EVO(0) 0x%08x, resetting... \n " , ctrl ) ;
2011-10-14 16:19:42 +10:00
/* deactivate both heads first, PDISP will disappear forever
* ( well , until you power cycle ) on some boards as soon as
* PMC_ENABLE is hit unless they are . .
*/
for ( i = 0 ; i < 2 ; i + + ) {
evo_icmd ( dev , 0 , 0x0880 + ( i * 0x400 ) , 0x05000000 ) ;
evo_icmd ( dev , 0 , 0x089c + ( i * 0x400 ) , 0 ) ;
evo_icmd ( dev , 0 , 0x0840 + ( i * 0x400 ) , 0 ) ;
evo_icmd ( dev , 0 , 0x0844 + ( i * 0x400 ) , 0 ) ;
evo_icmd ( dev , 0 , 0x085c + ( i * 0x400 ) , 0 ) ;
evo_icmd ( dev , 0 , 0x0874 + ( i * 0x400 ) , 0 ) ;
}
evo_icmd ( dev , 0 , 0x0080 , 0 ) ;
/* reset PDISP */
nv_mask ( dev , 0x000200 , 0x40000000 , 0x00000000 ) ;
nv_mask ( dev , 0x000200 , 0x40000000 , 0x40000000 ) ;
}
2011-10-29 11:31:29 +10:00
2010-07-24 17:37:33 +02:00
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 drm_nouveau_private * dev_priv = dev - > dev_private ;
struct nouveau_timer_engine * ptimer = & dev_priv - > engine . timer ;
struct nv50_display * disp = nv50_display ( dev ) ;
struct nouveau_channel * evo = disp - > master ;
u64 start ;
int ret ;
ret = RING_SPACE ( evo , 6 ) ;
if ( ret = = 0 ) {
BEGIN_RING ( evo , 0 , 0x0084 , 1 ) ;
OUT_RING ( evo , 0x80000000 ) ;
BEGIN_RING ( evo , 0 , 0x0080 , 1 ) ;
OUT_RING ( evo , 0 ) ;
BEGIN_RING ( evo , 0 , 0x0084 , 1 ) ;
OUT_RING ( evo , 0x00000000 ) ;
nv_wo32 ( disp - > ntfy , 0x000 , 0x00000000 ) ;
FIRE_RING ( evo ) ;
start = ptimer - > read ( dev ) ;
do {
if ( nv_ro32 ( disp - > ntfy , 0x000 ) )
return 0 ;
} while ( ptimer - > read ( dev ) - start < 2000000000ULL ) ;
}
return - EBUSY ;
}
2009-12-11 19:24:15 +10:00
int
nv50_display_init ( struct drm_device * dev )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2010-07-26 09:28:25 +10:00
struct nouveau_gpio_engine * pgpio = & dev_priv - > engine . gpio ;
2009-12-11 19:24:15 +10:00
struct drm_connector * connector ;
2010-10-19 13:05:51 +10:00
struct nouveau_channel * evo ;
2009-12-11 19:24:15 +10:00
int ret , i ;
2010-10-18 12:34:04 +10:00
u32 val ;
2009-12-11 19:24:15 +10:00
2009-12-13 16:53:12 +01:00
NV_DEBUG_KMS ( dev , " \n " ) ;
2009-12-11 19:24:15 +10:00
nv_wr32 ( dev , 0x00610184 , nv_rd32 ( dev , 0x00614004 ) ) ;
2010-10-19 11:14:17 +10:00
2009-12-11 19:24:15 +10:00
/*
* I think the 0x006101 XX range is some kind of main control area
* that enables things .
*/
/* CRTC? */
for ( i = 0 ; i < 2 ; i + + ) {
val = nv_rd32 ( dev , 0x00616100 + ( i * 0x800 ) ) ;
nv_wr32 ( dev , 0x00610190 + ( i * 0x10 ) , val ) ;
val = nv_rd32 ( dev , 0x00616104 + ( i * 0x800 ) ) ;
nv_wr32 ( dev , 0x00610194 + ( i * 0x10 ) , val ) ;
val = nv_rd32 ( dev , 0x00616108 + ( i * 0x800 ) ) ;
nv_wr32 ( dev , 0x00610198 + ( i * 0x10 ) , val ) ;
val = nv_rd32 ( dev , 0x0061610c + ( i * 0x800 ) ) ;
nv_wr32 ( dev , 0x0061019c + ( i * 0x10 ) , val ) ;
}
2010-10-19 11:14:17 +10:00
2009-12-11 19:24:15 +10:00
/* DAC */
for ( i = 0 ; i < 3 ; i + + ) {
val = nv_rd32 ( dev , 0x0061a000 + ( i * 0x800 ) ) ;
nv_wr32 ( dev , 0x006101d0 + ( i * 0x04 ) , val ) ;
}
2010-10-19 11:14:17 +10:00
2009-12-11 19:24:15 +10:00
/* SOR */
2010-09-06 11:39:25 +10:00
for ( i = 0 ; i < nv50_sor_nr ( dev ) ; i + + ) {
2009-12-11 19:24:15 +10:00
val = nv_rd32 ( dev , 0x0061c000 + ( i * 0x800 ) ) ;
nv_wr32 ( dev , 0x006101e0 + ( i * 0x04 ) , val ) ;
}
2010-10-19 11:14:17 +10:00
2010-09-06 11:39:25 +10:00
/* EXT */
2009-12-11 19:24:15 +10:00
for ( i = 0 ; i < 3 ; i + + ) {
val = nv_rd32 ( dev , 0x0061e000 + ( i * 0x800 ) ) ;
nv_wr32 ( dev , 0x006101f0 + ( i * 0x04 ) , val ) ;
}
for ( i = 0 ; i < 3 ; i + + ) {
nv_wr32 ( dev , NV50_PDISPLAY_DAC_DPMS_CTRL ( i ) , 0x00550000 |
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING ) ;
nv_wr32 ( dev , NV50_PDISPLAY_DAC_CLK_CTRL1 ( i ) , 0x00000001 ) ;
}
/* The precise purpose is unknown, i suspect it has something to do
* with text mode .
*/
if ( nv_rd32 ( dev , NV50_PDISPLAY_INTR_1 ) & 0x100 ) {
nv_wr32 ( dev , NV50_PDISPLAY_INTR_1 , 0x100 ) ;
nv_wr32 ( dev , 0x006194e8 , nv_rd32 ( dev , 0x006194e8 ) & ~ 1 ) ;
2010-09-07 17:34:44 +02:00
if ( ! nv_wait ( dev , 0x006194e8 , 2 , 0 ) ) {
2009-12-11 19:24:15 +10:00
NV_ERROR ( dev , " timeout: (0x6194e8 & 2) != 0 \n " ) ;
NV_ERROR ( dev , " 0x6194e8 = 0x%08x \n " ,
nv_rd32 ( dev , 0x6194e8 ) ) ;
return - EBUSY ;
}
}
2010-10-19 11:14:17 +10:00
for ( i = 0 ; i < 2 ; i + + ) {
nv_wr32 ( dev , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) , 0x2000 ) ;
if ( ! nv_wait ( dev , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ,
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS , 0 ) ) {
NV_ERROR ( dev , " timeout: CURSOR_CTRL2_STATUS == 0 \n " ) ;
NV_ERROR ( dev , " CURSOR_CTRL2 = 0x%08x \n " ,
nv_rd32 ( dev , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ) ) ;
return - EBUSY ;
}
nv_wr32 ( dev , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ,
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON ) ;
if ( ! nv_wait ( dev , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ,
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS ,
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE ) ) {
NV_ERROR ( dev , " timeout: "
" CURSOR_CTRL2_STATUS_ACTIVE(%d) \n " , i ) ;
NV_ERROR ( dev , " CURSOR_CTRL2(%d) = 0x%08x \n " , i ,
nv_rd32 ( dev , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ) ) ;
return - EBUSY ;
}
}
nv_wr32 ( dev , NV50_PDISPLAY_PIO_CTRL , 0x00000000 ) ;
nv_mask ( dev , NV50_PDISPLAY_INTR_0 , 0x00000000 , 0x00000000 ) ;
2010-10-20 14:23:29 +10:00
nv_wr32 ( dev , NV50_PDISPLAY_INTR_EN_0 , 0x00000000 ) ;
2010-10-19 11:14:17 +10:00
nv_mask ( dev , NV50_PDISPLAY_INTR_1 , 0x00000000 , 0x00000000 ) ;
2010-10-20 14:23:29 +10:00
nv_wr32 ( dev , NV50_PDISPLAY_INTR_EN_1 ,
NV50_PDISPLAY_INTR_EN_1_CLK_UNK10 |
NV50_PDISPLAY_INTR_EN_1_CLK_UNK20 |
NV50_PDISPLAY_INTR_EN_1_CLK_UNK40 ) ;
2010-10-19 11:14:17 +10:00
/* enable hotplug interrupts */
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
struct nouveau_connector * conn = nouveau_connector ( connector ) ;
if ( conn - > dcb - > gpio_tag = = 0xff )
continue ;
pgpio - > irq_enable ( dev , conn - > dcb - > gpio_tag , true ) ;
}
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
2010-10-19 13:05:51 +10:00
nv_wr32 ( dev , NV50_PDISPLAY_OBJECTS , ( evo - > ramin - > vinst > > 8 ) | 9 ) ;
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 ;
BEGIN_RING ( 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
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
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 ;
2009-12-13 16:53:12 +01:00
NV_DEBUG_KMS ( dev , " \n " ) ;
2009-12-11 19:24:15 +10:00
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 ) {
2011-02-01 10:07:32 +10:00
BEGIN_RING ( evo , 0 , NV50_EVO_UPDATE , 1 ) ;
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 ;
nv_wr32 ( dev , NV50_PDISPLAY_INTR_1 , mask ) ;
2010-09-07 17:34:44 +02:00
if ( ! nv_wait ( dev , NV50_PDISPLAY_INTR_1 , mask , mask ) ) {
2009-12-11 19:24:15 +10:00
NV_ERROR ( dev , " timeout: (0x610024 & 0x%08x) == "
" 0x%08x \n " , mask , mask ) ;
NV_ERROR ( dev , " 0x610024 = 0x%08x \n " ,
nv_rd32 ( dev , NV50_PDISPLAY_INTR_1 ) ) ;
}
}
2011-07-04 10:47:19 +10:00
for ( i = 0 ; i < 2 ; i + + ) {
nv_wr32 ( dev , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) , 0 ) ;
if ( ! nv_wait ( dev , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ,
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS , 0 ) ) {
NV_ERROR ( dev , " timeout: CURSOR_CTRL2_STATUS == 0 \n " ) ;
NV_ERROR ( dev , " CURSOR_CTRL2 = 0x%08x \n " ,
nv_rd32 ( dev , NV50_PDISPLAY_CURSOR_CURSOR_CTRL2 ( i ) ) ) ;
}
}
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 + + ) {
2010-09-07 17:34:44 +02:00
if ( ! nv_wait ( dev , NV50_PDISPLAY_SOR_DPMS_STATE ( i ) ,
2009-12-11 19:24:15 +10:00
NV50_PDISPLAY_SOR_DPMS_STATE_WAIT , 0 ) ) {
NV_ERROR ( dev , " timeout: SOR_DPMS_STATE_WAIT(%d) == 0 \n " , i ) ;
NV_ERROR ( dev , " SOR_DPMS_STATE(%d) = 0x%08x \n " , i ,
nv_rd32 ( dev , NV50_PDISPLAY_SOR_DPMS_STATE ( i ) ) ) ;
}
}
/* disable interrupts. */
2010-10-20 14:23:29 +10:00
nv_wr32 ( dev , NV50_PDISPLAY_INTR_EN_1 , 0x00000000 ) ;
2009-12-11 19:24:15 +10:00
/* disable hotplug interrupts */
nv_wr32 ( dev , 0xe054 , 0xffffffff ) ;
nv_wr32 ( dev , 0xe050 , 0x00000000 ) ;
if ( dev_priv - > chipset > = 0x90 ) {
nv_wr32 ( dev , 0xe074 , 0xffffffff ) ;
nv_wr32 ( dev , 0xe070 , 0x00000000 ) ;
}
}
2011-11-09 11:36:33 +10:00
int
nv50_display_create ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2010-02-24 10:03:05 +10:00
struct dcb_table * dcb = & dev_priv - > 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 11:36:33 +10:00
int i ;
2009-12-11 19:24:15 +10:00
2009-12-13 16:53:12 +01:00
NV_DEBUG_KMS ( dev , " \n " ) ;
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 ;
dev_priv - > engine . display . priv = priv ;
2009-12-11 19:24:15 +10:00
/* Create CRTC objects */
for ( i = 0 ; i < 2 ; i + + )
nv50_crtc_create ( dev , i ) ;
/* We setup the encoders from the BIOS table */
for ( i = 0 ; i < dcb - > entries ; i + + ) {
struct dcb_entry * entry = & dcb - > entry [ i ] ;
if ( entry - > location ! = DCB_LOC_ON_CHIP ) {
NV_WARN ( dev , " Off-chip encoder %d/%d unsupported \n " ,
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 ) {
case OUTPUT_TMDS :
case OUTPUT_LVDS :
case OUTPUT_DP :
2010-06-28 14:35:50 +10:00
nv50_sor_create ( connector , entry ) ;
2009-12-11 19:24:15 +10:00
break ;
case 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 :
NV_WARN ( dev , " DCB encoder %d unknown \n " , entry - > type ) ;
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 ] ) {
NV_WARN ( dev , " %s has no encoders, removing \n " ,
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 ) ;
2010-11-03 10:27:27 +10:00
nouveau_irq_register ( dev , 26 , nv50_display_isr ) ;
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
2009-12-13 16:53:12 +01:00
NV_DEBUG_KMS ( dev , " \n " ) ;
2009-12-11 19:24:15 +10:00
2010-11-03 10:27:27 +10:00
nouveau_irq_unregister ( dev , 26 ) ;
2011-02-01 10:07:32 +10:00
kfree ( disp ) ;
2009-12-11 19:24:15 +10:00
}
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 ;
}
BEGIN_RING ( evo , 0 , 0x0084 , 1 ) ;
OUT_RING ( evo , 0x00000000 ) ;
BEGIN_RING ( evo , 0 , 0x0094 , 1 ) ;
OUT_RING ( evo , 0x00000000 ) ;
BEGIN_RING ( evo , 0 , 0x00c0 , 1 ) ;
OUT_RING ( evo , 0x00000000 ) ;
BEGIN_RING ( evo , 0 , 0x0080 , 1 ) ;
OUT_RING ( evo , 0x00000000 ) ;
FIRE_RING ( evo ) ;
}
int
nv50_display_flip_next ( struct drm_crtc * crtc , struct drm_framebuffer * fb ,
struct nouveau_channel * chan )
{
struct drm_nouveau_private * dev_priv = crtc - > dev - > dev_private ;
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 ;
}
if ( dev_priv - > chipset < 0xc0 ) {
BEGIN_RING ( chan , NvSubSw , 0x0060 , 2 ) ;
OUT_RING ( chan , NvEvoSema0 + nv_crtc - > index ) ;
OUT_RING ( chan , dispc - > sem . offset ) ;
BEGIN_RING ( chan , NvSubSw , 0x006c , 1 ) ;
OUT_RING ( chan , 0xf00d0000 | dispc - > sem . value ) ;
BEGIN_RING ( chan , NvSubSw , 0x0064 , 2 ) ;
OUT_RING ( chan , dispc - > sem . offset ^ 0x10 ) ;
OUT_RING ( chan , 0x74b1e000 ) ;
BEGIN_RING ( chan , NvSubSw , 0x0060 , 1 ) ;
if ( dev_priv - > chipset < 0x84 )
OUT_RING ( chan , NvSema ) ;
else
OUT_RING ( chan , chan - > vram_handle ) ;
} else {
2011-06-07 15:43:31 +10:00
u64 offset = chan - > dispc_vma [ nv_crtc - > index ] . offset ;
offset + = dispc - > sem . offset ;
2011-02-07 13:29:23 +10:00
BEGIN_NVC0 ( chan , 2 , NvSubM2MF , 0x0010 , 4 ) ;
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 ) ;
BEGIN_NVC0 ( chan , 2 , NvSubM2MF , 0x0010 , 4 ) ;
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 */
BEGIN_RING ( evo , 0 , 0x0100 , 1 ) ;
OUT_RING ( evo , 0xfffe0000 ) ;
2011-06-16 14:40:27 +10:00
if ( chan ) {
BEGIN_RING ( evo , 0 , 0x0084 , 1 ) ;
OUT_RING ( evo , 0x00000100 ) ;
} else {
BEGIN_RING ( evo , 0 , 0x0084 , 1 ) ;
OUT_RING ( evo , 0x00000010 ) ;
/* allows gamma somehow, PDISP will bitch at you if
* you don ' t wait for vblank before changing this . .
*/
BEGIN_RING ( evo , 0 , 0x00e0 , 1 ) ;
OUT_RING ( evo , 0x40000000 ) ;
}
BEGIN_RING ( 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 ) ;
BEGIN_RING ( evo , 0 , 0x00a0 , 2 ) ;
OUT_RING ( evo , 0x00000000 ) ;
OUT_RING ( evo , 0x00000000 ) ;
BEGIN_RING ( evo , 0 , 0x00c0 , 1 ) ;
OUT_RING ( evo , nv_fb - > r_dma ) ;
BEGIN_RING ( evo , 0 , 0x0110 , 2 ) ;
OUT_RING ( evo , 0x00000000 ) ;
OUT_RING ( evo , 0x00000000 ) ;
BEGIN_RING ( 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 ) ;
BEGIN_RING ( evo , 0 , 0x0080 , 1 ) ;
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
nv50_display_script_select ( struct drm_device * dev , struct dcb_entry * dcb ,
u32 mc , int pxclk )
2009-12-11 19:24:15 +10:00
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2009-12-21 12:16:52 +10:00
struct nouveau_connector * nv_connector = NULL ;
struct drm_encoder * encoder ;
2010-02-24 10:03:05 +10:00
struct nvbios * bios = & dev_priv - > 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 ) {
2009-12-11 19:24:15 +10:00
case OUTPUT_LVDS :
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 & &
nv_connector - > dcb - > type = = DCB_CONNECTOR_LVDS_SPWG ) {
/* 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
}
if ( nouveau_uscript_lvds > = 0 ) {
NV_INFO ( dev , " override script 0x%04x with 0x%04x "
" for output LVDS-%d \n " , script ,
nouveau_uscript_lvds , or ) ;
script = nouveau_uscript_lvds ;
}
break ;
case OUTPUT_TMDS :
script = ( mc > > 8 ) & 0xf ;
if ( pxclk > = 165000 )
script | = 0x0100 ;
if ( nouveau_uscript_tmds > = 0 ) {
NV_INFO ( dev , " override script 0x%04x with 0x%04x "
" for output TMDS-%d \n " , script ,
nouveau_uscript_tmds , or ) ;
script = nouveau_uscript_tmds ;
}
break ;
case OUTPUT_DP :
script = ( mc > > 8 ) & 0xf ;
break ;
case OUTPUT_ANALOG :
script = 0xff ;
break ;
default :
NV_ERROR ( dev , " modeset on unsupported output type! \n " ) ;
break ;
}
return script ;
}
static void
nv50_display_vblank_crtc_handler ( struct drm_device * dev , int crtc )
{
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2010-10-21 18:19:29 +02:00
struct nouveau_channel * chan , * tmp ;
2009-12-11 19:24:15 +10:00
2010-10-21 18:19:29 +02:00
list_for_each_entry_safe ( chan , tmp , & dev_priv - > vbl_waiting ,
nvsw . vbl_wait ) {
2010-10-24 14:15:58 +02:00
if ( chan - > nvsw . vblsem_head ! = crtc )
continue ;
2009-12-11 19:24:15 +10:00
nouveau_bo_wr32 ( chan - > notifier_bo , chan - > nvsw . vblsem_offset ,
chan - > nvsw . vblsem_rval ) ;
list_del ( & chan - > nvsw . vbl_wait ) ;
2010-10-21 18:19:29 +02:00
drm_vblank_put ( dev , crtc ) ;
2009-12-11 19:24:15 +10:00
}
2010-10-21 18:19:29 +02:00
drm_handle_vblank ( dev , crtc ) ;
2009-12-11 19:24:15 +10:00
}
static void
nv50_display_vblank_handler ( struct drm_device * dev , uint32_t intr )
{
if ( intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0 )
nv50_display_vblank_crtc_handler ( dev , 0 ) ;
if ( intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1 )
nv50_display_vblank_crtc_handler ( dev , 1 ) ;
2010-10-21 18:19:29 +02:00
nv_wr32 ( dev , NV50_PDISPLAY_INTR_1 , NV50_PDISPLAY_INTR_1_VBLANK_CRTC ) ;
2009-12-11 19:24:15 +10:00
}
static void
nv50_display_unk10_handler ( struct drm_device * dev )
{
2010-07-06 08:54:34 +10:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2011-02-01 10:07:32 +10:00
struct nv50_display * disp = nv50_display ( dev ) ;
2010-07-06 08:54:34 +10:00
u32 unk30 = nv_rd32 ( dev , 0x610030 ) , mc ;
2011-11-09 15:30:08 +10:00
int i , crtc , or = 0 , type = OUTPUT_ANY ;
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
NV_DEBUG_KMS ( dev , " 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
nv_wr32 ( dev , 0x619494 , nv_rd32 ( dev , 0x619494 ) & ~ 8 ) ;
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 */
for ( i = 0 ; type = = OUTPUT_ANY & & i < 3 ; i + + ) {
mc = nv_rd32 ( dev , NV50_PDISPLAY_DAC_MODE_CTRL_C ( i ) ) ;
NV_DEBUG_KMS ( dev , " DAC-%d mc: 0x%08x \n " , i , mc ) ;
if ( ! ( mc & ( 1 < < crtc ) ) )
continue ;
switch ( ( mc & 0x00000f00 ) > > 8 ) {
case 0 : type = OUTPUT_ANALOG ; break ;
case 1 : type = OUTPUT_TV ; break ;
default :
NV_ERROR ( dev , " invalid mc, DAC-%d: 0x%08x \n " , i , mc ) ;
goto ack ;
}
or = i ;
}
2010-09-06 11:39:25 +10:00
for ( i = 0 ; type = = OUTPUT_ANY & & i < nv50_sor_nr ( dev ) ; i + + ) {
2010-07-06 08:54:34 +10:00
if ( dev_priv - > chipset < 0x90 | |
dev_priv - > chipset = = 0x92 | |
dev_priv - > chipset = = 0xa0 )
mc = nv_rd32 ( dev , NV50_PDISPLAY_SOR_MODE_CTRL_C ( i ) ) ;
else
mc = nv_rd32 ( dev , NV90_PDISPLAY_SOR_MODE_CTRL_C ( i ) ) ;
NV_DEBUG_KMS ( dev , " SOR-%d mc: 0x%08x \n " , i , mc ) ;
if ( ! ( mc & ( 1 < < crtc ) ) )
continue ;
switch ( ( mc & 0x00000f00 ) > > 8 ) {
case 0 : type = OUTPUT_LVDS ; break ;
case 1 : type = OUTPUT_TMDS ; break ;
case 2 : type = OUTPUT_TMDS ; break ;
case 5 : type = OUTPUT_TMDS ; break ;
case 8 : type = OUTPUT_DP ; break ;
case 9 : type = OUTPUT_DP ; break ;
default :
NV_ERROR ( dev , " invalid mc, SOR-%d: 0x%08x \n " , i , mc ) ;
goto ack ;
}
or = i ;
}
/* There was no encoder to disable */
if ( type = = OUTPUT_ANY )
goto ack ;
/* Disable the encoder */
for ( i = 0 ; i < dev_priv - > vbios . dcb . entries ; i + + ) {
struct dcb_entry * dcb = & dev_priv - > vbios . dcb . entry [ i ] ;
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 ;
}
}
NV_ERROR ( dev , " no dcb for %d %d 0x%08x \n " , or , type , mc ) ;
2009-12-11 19:24:15 +10:00
ack :
nv_wr32 ( dev , NV50_PDISPLAY_INTR_1 , NV50_PDISPLAY_INTR_1_CLK_UNK10 ) ;
nv_wr32 ( dev , 0x610030 , 0x80000000 ) ;
}
static void
nv50_display_unk20_handler ( struct drm_device * dev )
{
2010-07-06 08:54:34 +10:00
struct drm_nouveau_private * dev_priv = dev - > dev_private ;
2011-02-01 10:07:32 +10:00
struct nv50_display * disp = nv50_display ( dev ) ;
2011-01-31 08:26:04 +10:00
u32 unk30 = nv_rd32 ( dev , 0x610030 ) , tmp , pclk , script , mc = 0 ;
2010-07-06 08:54:34 +10:00
struct dcb_entry * dcb ;
2011-11-09 15:30:08 +10:00
int i , crtc , or = 0 , type = OUTPUT_ANY ;
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
NV_DEBUG_KMS ( dev , " 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 ) {
pclk = nv_rd32 ( dev , NV50_PDISPLAY_CRTC_P ( crtc , CLOCK ) ) ;
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
tmp = nv_rd32 ( dev , NV50_PDISPLAY_CRTC_CLK_CTRL2 ( crtc ) ) ;
tmp & = ~ 0x000000f ;
nv_wr32 ( dev , NV50_PDISPLAY_CRTC_CLK_CTRL2 ( crtc ) , tmp ) ;
}
/* 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 ;
2010-07-06 08:54:34 +10:00
pclk = nv_rd32 ( dev , 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 */
for ( i = 0 ; type = = OUTPUT_ANY & & i < 3 ; i + + ) {
mc = nv_rd32 ( dev , NV50_PDISPLAY_DAC_MODE_CTRL_P ( i ) ) ;
NV_DEBUG_KMS ( dev , " DAC-%d mc: 0x%08x \n " , i , mc ) ;
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 ) {
case 0 : type = OUTPUT_ANALOG ; break ;
case 1 : type = OUTPUT_TV ; break ;
default :
NV_ERROR ( dev , " invalid mc, DAC-%d: 0x%08x \n " , i , mc ) ;
goto ack ;
}
or = i ;
}
2010-09-06 11:39:25 +10:00
for ( i = 0 ; type = = OUTPUT_ANY & & i < nv50_sor_nr ( dev ) ; i + + ) {
2010-07-06 08:54:34 +10:00
if ( dev_priv - > chipset < 0x90 | |
dev_priv - > chipset = = 0x92 | |
dev_priv - > chipset = = 0xa0 )
mc = nv_rd32 ( dev , NV50_PDISPLAY_SOR_MODE_CTRL_P ( i ) ) ;
else
mc = nv_rd32 ( dev , NV90_PDISPLAY_SOR_MODE_CTRL_P ( i ) ) ;
NV_DEBUG_KMS ( dev , " SOR-%d mc: 0x%08x \n " , i , mc ) ;
if ( ! ( mc & ( 1 < < crtc ) ) )
continue ;
switch ( ( mc & 0x00000f00 ) > > 8 ) {
case 0 : type = OUTPUT_LVDS ; break ;
case 1 : type = OUTPUT_TMDS ; break ;
case 2 : type = OUTPUT_TMDS ; break ;
case 5 : type = OUTPUT_TMDS ; break ;
case 8 : type = OUTPUT_DP ; break ;
case 9 : type = OUTPUT_DP ; break ;
default :
NV_ERROR ( dev , " invalid mc, SOR-%d: 0x%08x \n " , i , mc ) ;
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
2010-07-06 08:54:34 +10:00
if ( type = = OUTPUT_ANY )
goto ack ;
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
/* Enable the encoder */
for ( i = 0 ; i < dev_priv - > vbios . dcb . entries ; i + + ) {
dcb = & dev_priv - > vbios . dcb . entry [ i ] ;
if ( dcb - > type = = type & & ( dcb - > or & ( 1 < < or ) ) )
break ;
}
2010-04-23 08:21:48 +10:00
2010-07-06 08:54:34 +10:00
if ( i = = dev_priv - > vbios . dcb . entries ) {
NV_ERROR ( dev , " no dcb for %d %d 0x%08x \n " , or , type , mc ) ;
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
2011-07-01 15:51:49 +10:00
if ( type = = OUTPUT_DP ) {
int link = ! ( dcb - > dpconf . sor . link & 1 ) ;
if ( ( mc & 0x000f0000 ) = = 0x00020000 )
nouveau_dp_tu_update ( dev , or , link , pclk , 18 ) ;
else
nouveau_dp_tu_update ( dev , or , link , pclk , 24 ) ;
}
2010-07-06 08:54:34 +10:00
if ( dcb - > type ! = OUTPUT_ANALOG ) {
2009-12-11 19:24:15 +10:00
tmp = nv_rd32 ( dev , NV50_PDISPLAY_SOR_CLK_CTRL2 ( or ) ) ;
tmp & = ~ 0x00000f0f ;
if ( script & 0x0100 )
tmp | = 0x00000101 ;
nv_wr32 ( dev , NV50_PDISPLAY_SOR_CLK_CTRL2 ( or ) , tmp ) ;
} else {
nv_wr32 ( dev , NV50_PDISPLAY_DAC_CLK_CTRL2 ( or ) , 0 ) ;
}
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 :
nv_wr32 ( dev , NV50_PDISPLAY_INTR_1 , NV50_PDISPLAY_INTR_1_CLK_UNK20 ) ;
nv_wr32 ( dev , 0x610030 , 0x80000000 ) ;
}
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
nv50_display_unk40_dp_set_tmds ( struct drm_device * dev , struct dcb_entry * dcb )
{
int or = ffs ( dcb - > or ) - 1 , link = ! ( dcb - > dpconf . sor . link & 1 ) ;
struct drm_encoder * encoder ;
u32 tmp ;
if ( dcb - > type ! = OUTPUT_TMDS )
return ;
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
struct nouveau_encoder * nv_encoder = nouveau_encoder ( encoder ) ;
if ( nv_encoder - > dcb - > type = = OUTPUT_DP & &
nv_encoder - > dcb - > or & ( 1 < < or ) ) {
tmp = nv_rd32 ( dev , NV50_SOR_DP_CTRL ( or , link ) ) ;
tmp & = ~ NV50_SOR_DP_CTRL_ENABLED ;
nv_wr32 ( dev , NV50_SOR_DP_CTRL ( or , link ) , tmp ) ;
break ;
}
}
}
2009-12-11 19:24:15 +10:00
static void
nv50_display_unk40_handler ( struct drm_device * dev )
{
2011-02-01 10:07:32 +10:00
struct nv50_display * disp = nv50_display ( dev ) ;
struct dcb_entry * dcb = disp - > irq . dcb ;
u16 script = disp - > irq . script ;
u32 unk30 = nv_rd32 ( dev , 0x610030 ) , pclk = disp - > irq . pclk ;
2009-12-11 19:24:15 +10:00
2010-07-06 08:54:34 +10:00
NV_DEBUG_KMS ( dev , " 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 :
nv_wr32 ( dev , NV50_PDISPLAY_INTR_1 , NV50_PDISPLAY_INTR_1_CLK_UNK40 ) ;
nv_wr32 ( dev , 0x610030 , 0x80000000 ) ;
nv_wr32 ( dev , 0x619494 , nv_rd32 ( dev , 0x619494 ) | 8 ) ;
}
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 ;
2009-12-11 19:24:15 +10:00
for ( ; ; ) {
uint32_t intr0 = nv_rd32 ( dev , NV50_PDISPLAY_INTR_0 ) ;
uint32_t intr1 = nv_rd32 ( dev , NV50_PDISPLAY_INTR_1 ) ;
2009-12-13 16:53:12 +01:00
NV_DEBUG_KMS ( dev , " 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 ;
}
nv_wr32 ( dev , NV03_PMC_INTR_EN_0 , 1 ) ;
}
static void
nv50_display_error_handler ( struct drm_device * dev )
{
2010-10-20 14:23:29 +10:00
u32 channels = ( nv_rd32 ( dev , NV50_PDISPLAY_INTR_0 ) & 0x001f0000 ) > > 16 ;
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
2010-10-20 14:23:29 +10:00
nv_wr32 ( dev , NV50_PDISPLAY_INTR_0 , 0x00010000 < < chid ) ;
addr = nv_rd32 ( dev , NV50_PDISPLAY_TRAPPED_ADDR ( chid ) ) ;
data = nv_rd32 ( dev , NV50_PDISPLAY_TRAPPED_DATA ( chid ) ) ;
NV_ERROR ( dev , " EvoCh %d Mthd 0x%04x Data 0x%08x "
" (0x%04x 0x%02x) \n " , chid ,
addr & 0xffc , data , addr > > 16 , ( addr > > 12 ) & 0xf ) ;
2009-12-11 19:24:15 +10:00
2010-10-20 14:23:29 +10:00
nv_wr32 ( dev , NV50_PDISPLAY_TRAPPED_ADDR ( chid ) , 0x90000000 ) ;
}
2009-12-11 19:24:15 +10:00
}
2010-11-03 10:27:27 +10:00
static void
nv50_display_isr ( struct drm_device * dev )
2009-12-11 19:24:15 +10:00
{
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 ;
while ( nv_rd32 ( dev , NV50_PMC_INTR_0 ) & NV50_PMC_INTR_0_DISPLAY ) {
uint32_t intr0 = nv_rd32 ( dev , NV50_PDISPLAY_INTR_0 ) ;
uint32_t intr1 = nv_rd32 ( dev , NV50_PDISPLAY_INTR_1 ) ;
uint32_t clock ;
2009-12-13 16:53:12 +01:00
NV_DEBUG_KMS ( dev , " 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 ) {
nv50_display_vblank_handler ( dev , intr1 ) ;
intr1 & = ~ NV50_PDISPLAY_INTR_1_VBLANK_CRTC ;
}
clock = ( intr1 & ( NV50_PDISPLAY_INTR_1_CLK_UNK10 |
NV50_PDISPLAY_INTR_1_CLK_UNK20 |
NV50_PDISPLAY_INTR_1_CLK_UNK40 ) ) ;
if ( clock ) {
nv_wr32 ( dev , 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 ) {
NV_ERROR ( dev , " unknown PDISPLAY_INTR_0: 0x%08x \n " , intr0 ) ;
nv_wr32 ( dev , NV50_PDISPLAY_INTR_0 , intr0 ) ;
}
if ( intr1 ) {
NV_ERROR ( dev ,
" unknown PDISPLAY_INTR_1: 0x%08x \n " , intr1 ) ;
nv_wr32 ( dev , NV50_PDISPLAY_INTR_1 , intr1 ) ;
}
}
}