2005-04-16 15:20:36 -07:00
# include "radeonfb.h"
# include "../edid.h"
static struct fb_var_screeninfo radeonfb_default_var = {
. xres = 640 ,
. yres = 480 ,
. xres_virtual = 640 ,
. yres_virtual = 480 ,
. bits_per_pixel = 8 ,
. red = { . length = 8 } ,
. green = { . length = 8 } ,
. blue = { . length = 8 } ,
. activate = FB_ACTIVATE_NOW ,
. height = - 1 ,
. width = - 1 ,
. pixclock = 39721 ,
. left_margin = 40 ,
. right_margin = 24 ,
. upper_margin = 32 ,
. lower_margin = 11 ,
. hsync_len = 96 ,
. vsync_len = 2 ,
. vmode = FB_VMODE_NONINTERLACED
} ;
static char * radeon_get_mon_name ( int type )
{
char * pret = NULL ;
switch ( type ) {
case MT_NONE :
pret = " no " ;
break ;
case MT_CRT :
pret = " CRT " ;
break ;
case MT_DFP :
pret = " DFP " ;
break ;
case MT_LCD :
pret = " LCD " ;
break ;
case MT_CTV :
pret = " CTV " ;
break ;
case MT_STV :
pret = " STV " ;
break ;
}
return pret ;
}
# ifdef CONFIG_PPC_OF
/*
* Try to find monitor informations & EDID data out of the Open Firmware
* device - tree . This also contains some " hacks " to work around a few machine
* models with broken OF probing by hard - coding known EDIDs for some Mac
* laptops internal LVDS panel . ( XXX : not done yet )
*/
static int __devinit radeon_parse_montype_prop ( struct device_node * dp , u8 * * out_EDID ,
int hdno )
{
static char * propnames [ ] = { " DFP,EDID " , " LCD,EDID " , " EDID " ,
" EDID1 " , " EDID2 " , NULL } ;
2006-07-12 15:40:40 +10:00
const u8 * pedid = NULL ;
const u8 * pmt = NULL ;
2005-04-16 15:20:36 -07:00
u8 * tmp ;
int i , mt = MT_NONE ;
RTRACE ( " analyzing OF properties... \n " ) ;
2006-07-12 15:40:40 +10:00
pmt = get_property ( dp , " display-type " , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( ! pmt )
return MT_NONE ;
RTRACE ( " display-type: %s \n " , pmt ) ;
/* OF says "LCD" for DFP as well, we discriminate from the caller of this
* function
*/
if ( ! strcmp ( pmt , " LCD " ) | | ! strcmp ( pmt , " DFP " ) )
mt = MT_DFP ;
else if ( ! strcmp ( pmt , " CRT " ) )
mt = MT_CRT ;
else {
if ( strcmp ( pmt , " NONE " ) ! = 0 )
printk ( KERN_WARNING " radeonfb: Unknown OF display-type: %s \n " ,
pmt ) ;
return MT_NONE ;
}
for ( i = 0 ; propnames [ i ] ! = NULL ; + + i ) {
2006-07-12 15:40:40 +10:00
pedid = get_property ( dp , propnames [ i ] , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( pedid ! = NULL )
break ;
}
/* We didn't find the EDID in the leaf node, some cards will actually
* put EDID1 / EDID2 in the parent , look for these ( typically M6 tipb ) .
* single - head cards have hdno = = - 1 and skip this step
*/
if ( pedid = = NULL & & dp - > parent & & ( hdno ! = - 1 ) )
pedid = get_property ( dp - > parent , ( hdno = = 0 ) ? " EDID1 " : " EDID2 " , NULL ) ;
if ( pedid = = NULL & & dp - > parent & & ( hdno = = 0 ) )
pedid = get_property ( dp - > parent , " EDID " , NULL ) ;
if ( pedid = = NULL )
return mt ;
tmp = ( u8 * ) kmalloc ( EDID_LENGTH , GFP_KERNEL ) ;
if ( ! tmp )
return mt ;
memcpy ( tmp , pedid , EDID_LENGTH ) ;
* out_EDID = tmp ;
return mt ;
}
static int __devinit radeon_probe_OF_head ( struct radeonfb_info * rinfo , int head_no ,
u8 * * out_EDID )
{
struct device_node * dp ;
RTRACE ( " radeon_probe_OF_head \n " ) ;
dp = rinfo - > of_node ;
while ( dp = = NULL )
return MT_NONE ;
if ( rinfo - > has_CRTC2 ) {
2006-07-12 15:40:40 +10:00
const char * pname ;
2005-04-16 15:20:36 -07:00
int len , second = 0 ;
dp = dp - > child ;
do {
if ( ! dp )
return MT_NONE ;
2006-07-12 15:40:40 +10:00
pname = get_property ( dp , " name " , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( ! pname )
return MT_NONE ;
len = strlen ( pname ) ;
RTRACE ( " head: %s (letter: %c, head_no: %d) \n " ,
pname , pname [ len - 1 ] , head_no ) ;
if ( pname [ len - 1 ] = = ' A ' & & head_no = = 0 ) {
int mt = radeon_parse_montype_prop ( dp , out_EDID , 0 ) ;
/* Maybe check for LVDS_GEN_CNTL here ? I need to check out
* what OF does when booting with lid closed
*/
if ( mt = = MT_DFP & & rinfo - > is_mobility )
mt = MT_LCD ;
return mt ;
} else if ( pname [ len - 1 ] = = ' B ' & & head_no = = 1 )
return radeon_parse_montype_prop ( dp , out_EDID , 1 ) ;
second = 1 ;
dp = dp - > sibling ;
} while ( ! second ) ;
} else {
if ( head_no > 0 )
return MT_NONE ;
return radeon_parse_montype_prop ( dp , out_EDID , - 1 ) ;
}
return MT_NONE ;
}
# endif /* CONFIG_PPC_OF */
static int __devinit radeon_get_panel_info_BIOS ( struct radeonfb_info * rinfo )
{
unsigned long tmp , tmp0 ;
char stmp [ 30 ] ;
int i ;
if ( ! rinfo - > bios_seg )
return 0 ;
if ( ! ( tmp = BIOS_IN16 ( rinfo - > fp_bios_start + 0x40 ) ) ) {
printk ( KERN_ERR " radeonfb: Failed to detect DFP panel info using BIOS \n " ) ;
rinfo - > panel_info . pwr_delay = 200 ;
return 0 ;
}
for ( i = 0 ; i < 24 ; i + + )
stmp [ i ] = BIOS_IN8 ( tmp + i + 1 ) ;
stmp [ 24 ] = 0 ;
printk ( " radeonfb: panel ID string: %s \n " , stmp ) ;
rinfo - > panel_info . xres = BIOS_IN16 ( tmp + 25 ) ;
rinfo - > panel_info . yres = BIOS_IN16 ( tmp + 27 ) ;
printk ( " radeonfb: detected LVDS panel size from BIOS: %dx%d \n " ,
rinfo - > panel_info . xres , rinfo - > panel_info . yres ) ;
rinfo - > panel_info . pwr_delay = BIOS_IN16 ( tmp + 44 ) ;
RTRACE ( " BIOS provided panel power delay: %d \n " , rinfo - > panel_info . pwr_delay ) ;
if ( rinfo - > panel_info . pwr_delay > 2000 | | rinfo - > panel_info . pwr_delay < = 0 )
rinfo - > panel_info . pwr_delay = 2000 ;
/*
* Some panels only work properly with some divider combinations
*/
rinfo - > panel_info . ref_divider = BIOS_IN16 ( tmp + 46 ) ;
rinfo - > panel_info . post_divider = BIOS_IN8 ( tmp + 48 ) ;
rinfo - > panel_info . fbk_divider = BIOS_IN16 ( tmp + 49 ) ;
if ( rinfo - > panel_info . ref_divider ! = 0 & &
rinfo - > panel_info . fbk_divider > 3 ) {
rinfo - > panel_info . use_bios_dividers = 1 ;
printk ( KERN_INFO " radeondb: BIOS provided dividers will be used \n " ) ;
RTRACE ( " ref_divider = %x \n " , rinfo - > panel_info . ref_divider ) ;
RTRACE ( " post_divider = %x \n " , rinfo - > panel_info . post_divider ) ;
RTRACE ( " fbk_divider = %x \n " , rinfo - > panel_info . fbk_divider ) ;
}
RTRACE ( " Scanning BIOS table ... \n " ) ;
for ( i = 0 ; i < 32 ; i + + ) {
tmp0 = BIOS_IN16 ( tmp + 64 + i * 2 ) ;
if ( tmp0 = = 0 )
break ;
RTRACE ( " %d x %d \n " , BIOS_IN16 ( tmp0 ) , BIOS_IN16 ( tmp0 + 2 ) ) ;
if ( ( BIOS_IN16 ( tmp0 ) = = rinfo - > panel_info . xres ) & &
( BIOS_IN16 ( tmp0 + 2 ) = = rinfo - > panel_info . yres ) ) {
rinfo - > panel_info . hblank = ( BIOS_IN16 ( tmp0 + 17 ) - BIOS_IN16 ( tmp0 + 19 ) ) * 8 ;
rinfo - > panel_info . hOver_plus = ( ( BIOS_IN16 ( tmp0 + 21 ) -
BIOS_IN16 ( tmp0 + 19 ) - 1 ) * 8 ) & 0x7fff ;
rinfo - > panel_info . hSync_width = BIOS_IN8 ( tmp0 + 23 ) * 8 ;
rinfo - > panel_info . vblank = BIOS_IN16 ( tmp0 + 24 ) - BIOS_IN16 ( tmp0 + 26 ) ;
rinfo - > panel_info . vOver_plus = ( BIOS_IN16 ( tmp0 + 28 ) & 0x7ff ) - BIOS_IN16 ( tmp0 + 26 ) ;
rinfo - > panel_info . vSync_width = ( BIOS_IN16 ( tmp0 + 28 ) & 0xf800 ) > > 11 ;
rinfo - > panel_info . clock = BIOS_IN16 ( tmp0 + 9 ) ;
/* Assume high active syncs for now until ATI tells me more... maybe we
* can probe register values here ?
*/
rinfo - > panel_info . hAct_high = 1 ;
rinfo - > panel_info . vAct_high = 1 ;
/* Mark panel infos valid */
rinfo - > panel_info . valid = 1 ;
RTRACE ( " Found panel in BIOS table: \n " ) ;
RTRACE ( " hblank: %d \n " , rinfo - > panel_info . hblank ) ;
RTRACE ( " hOver_plus: %d \n " , rinfo - > panel_info . hOver_plus ) ;
RTRACE ( " hSync_width: %d \n " , rinfo - > panel_info . hSync_width ) ;
RTRACE ( " vblank: %d \n " , rinfo - > panel_info . vblank ) ;
RTRACE ( " vOver_plus: %d \n " , rinfo - > panel_info . vOver_plus ) ;
RTRACE ( " vSync_width: %d \n " , rinfo - > panel_info . vSync_width ) ;
RTRACE ( " clock: %d \n " , rinfo - > panel_info . clock ) ;
return 1 ;
}
}
RTRACE ( " Didn't find panel in BIOS table ! \n " ) ;
return 0 ;
}
/* Try to extract the connector informations from the BIOS. This
* doesn ' t quite work yet , but it ' s output is still useful for
* debugging
*/
static void __devinit radeon_parse_connector_info ( struct radeonfb_info * rinfo )
{
int offset , chips , connectors , tmp , i , conn , type ;
static char * __conn_type_table [ 16 ] = {
" NONE " , " Proprietary " , " CRT " , " DVI-I " , " DVI-D " , " Unknown " , " Unknown " ,
" Unknown " , " Unknown " , " Unknown " , " Unknown " , " Unknown " , " Unknown " ,
" Unknown " , " Unknown " , " Unknown "
} ;
if ( ! rinfo - > bios_seg )
return ;
offset = BIOS_IN16 ( rinfo - > fp_bios_start + 0x50 ) ;
if ( offset = = 0 ) {
printk ( KERN_WARNING " radeonfb: No connector info table detected \n " ) ;
return ;
}
/* Don't do much more at this point but displaying the data if
* DEBUG is enabled
*/
chips = BIOS_IN8 ( offset + + ) > > 4 ;
RTRACE ( " %d chips in connector info \n " , chips ) ;
for ( i = 0 ; i < chips ; i + + ) {
tmp = BIOS_IN8 ( offset + + ) ;
connectors = tmp & 0x0f ;
RTRACE ( " - chip %d has %d connectors \n " , tmp > > 4 , connectors ) ;
for ( conn = 0 ; ; conn + + ) {
tmp = BIOS_IN16 ( offset ) ;
if ( tmp = = 0 )
break ;
offset + = 2 ;
type = ( tmp > > 12 ) & 0x0f ;
RTRACE ( " * connector %d of type %d (%s) : %04x \n " ,
conn , type , __conn_type_table [ type ] , tmp ) ;
}
}
}
/*
* Probe physical connection of a CRT . This code comes from XFree
* as well and currently is only implemented for the CRT DAC , the
* code for the TVDAC is commented out in XFree as " non working "
*/
static int __devinit radeon_crt_is_connected ( struct radeonfb_info * rinfo , int is_crt_dac )
{
int connected = 0 ;
/* the monitor either wasn't connected or it is a non-DDC CRT.
* try to probe it
*/
if ( is_crt_dac ) {
unsigned long ulOrigVCLK_ECP_CNTL ;
unsigned long ulOrigDAC_CNTL ;
unsigned long ulOrigDAC_EXT_CNTL ;
unsigned long ulOrigCRTC_EXT_CNTL ;
unsigned long ulData ;
unsigned long ulMask ;
ulOrigVCLK_ECP_CNTL = INPLL ( VCLK_ECP_CNTL ) ;
ulData = ulOrigVCLK_ECP_CNTL ;
ulData & = ~ ( PIXCLK_ALWAYS_ONb
| PIXCLK_DAC_ALWAYS_ONb ) ;
ulMask = ~ ( PIXCLK_ALWAYS_ONb
| PIXCLK_DAC_ALWAYS_ONb ) ;
OUTPLLP ( VCLK_ECP_CNTL , ulData , ulMask ) ;
ulOrigCRTC_EXT_CNTL = INREG ( CRTC_EXT_CNTL ) ;
ulData = ulOrigCRTC_EXT_CNTL ;
ulData | = CRTC_CRT_ON ;
OUTREG ( CRTC_EXT_CNTL , ulData ) ;
ulOrigDAC_EXT_CNTL = INREG ( DAC_EXT_CNTL ) ;
ulData = ulOrigDAC_EXT_CNTL ;
ulData & = ~ DAC_FORCE_DATA_MASK ;
ulData | = ( DAC_FORCE_BLANK_OFF_EN
| DAC_FORCE_DATA_EN
| DAC_FORCE_DATA_SEL_MASK ) ;
if ( ( rinfo - > family = = CHIP_FAMILY_RV250 ) | |
( rinfo - > family = = CHIP_FAMILY_RV280 ) )
ulData | = ( 0x01b6 < < DAC_FORCE_DATA_SHIFT ) ;
else
ulData | = ( 0x01ac < < DAC_FORCE_DATA_SHIFT ) ;
OUTREG ( DAC_EXT_CNTL , ulData ) ;
ulOrigDAC_CNTL = INREG ( DAC_CNTL ) ;
ulData = ulOrigDAC_CNTL ;
ulData | = DAC_CMP_EN ;
ulData & = ~ ( DAC_RANGE_CNTL_MASK
| DAC_PDWN ) ;
ulData | = 0x2 ;
OUTREG ( DAC_CNTL , ulData ) ;
mdelay ( 1 ) ;
ulData = INREG ( DAC_CNTL ) ;
connected = ( DAC_CMP_OUTPUT & ulData ) ? 1 : 0 ;
ulData = ulOrigVCLK_ECP_CNTL ;
ulMask = 0xFFFFFFFFL ;
OUTPLLP ( VCLK_ECP_CNTL , ulData , ulMask ) ;
OUTREG ( DAC_CNTL , ulOrigDAC_CNTL ) ;
OUTREG ( DAC_EXT_CNTL , ulOrigDAC_EXT_CNTL ) ;
OUTREG ( CRTC_EXT_CNTL , ulOrigCRTC_EXT_CNTL ) ;
}
return connected ? MT_CRT : MT_NONE ;
}
/*
* Parse the " monitor_layout " string if any . This code is mostly
* copied from XFree ' s radeon driver
*/
static int __devinit radeon_parse_monitor_layout ( struct radeonfb_info * rinfo ,
const char * monitor_layout )
{
char s1 [ 5 ] , s2 [ 5 ] ;
int i = 0 , second = 0 ;
const char * s ;
if ( ! monitor_layout )
return 0 ;
s = monitor_layout ;
do {
switch ( * s ) {
case ' , ' :
s1 [ i ] = ' \0 ' ;
i = 0 ;
second = 1 ;
break ;
case ' ' :
case ' \0 ' :
break ;
default :
if ( i > 4 )
break ;
if ( second )
s2 [ i ] = * s ;
else
s1 [ i ] = * s ;
i + + ;
}
2006-03-11 03:27:28 -08:00
if ( i > 4 )
i = 4 ;
2005-04-16 15:20:36 -07:00
} while ( * s + + ) ;
if ( second )
s2 [ i ] = 0 ;
else {
s1 [ i ] = 0 ;
s2 [ 0 ] = 0 ;
}
if ( strcmp ( s1 , " CRT " ) = = 0 )
rinfo - > mon1_type = MT_CRT ;
else if ( strcmp ( s1 , " TMDS " ) = = 0 )
rinfo - > mon1_type = MT_DFP ;
else if ( strcmp ( s1 , " LVDS " ) = = 0 )
rinfo - > mon1_type = MT_LCD ;
if ( strcmp ( s2 , " CRT " ) = = 0 )
rinfo - > mon2_type = MT_CRT ;
else if ( strcmp ( s2 , " TMDS " ) = = 0 )
rinfo - > mon2_type = MT_DFP ;
else if ( strcmp ( s2 , " LVDS " ) = = 0 )
rinfo - > mon2_type = MT_LCD ;
return 1 ;
}
/*
* Probe display on both primary and secondary card ' s connector ( if any )
* by various available techniques ( i2c , OF device tree , BIOS , . . . ) and
2006-01-10 00:10:13 +01:00
* try to retrieve EDID . The algorithm here comes from XFree ' s radeon
2005-04-16 15:20:36 -07:00
* driver
*/
void __devinit radeon_probe_screens ( struct radeonfb_info * rinfo ,
const char * monitor_layout , int ignore_edid )
{
# ifdef CONFIG_FB_RADEON_I2C
int ddc_crt2_used = 0 ;
# endif
int tmp , i ;
radeon_parse_connector_info ( rinfo ) ;
if ( radeon_parse_monitor_layout ( rinfo , monitor_layout ) ) {
/*
* If user specified a monitor_layout option , use it instead
* of auto - detecting . Maybe we should only use this argument
* on the first radeon card probed or provide a way to specify
* a layout for each card ?
*/
RTRACE ( " Using specified monitor layout: %s " , monitor_layout ) ;
# ifdef CONFIG_FB_RADEON_I2C
if ( ! ignore_edid ) {
if ( rinfo - > mon1_type ! = MT_NONE )
if ( ! radeon_probe_i2c_connector ( rinfo , ddc_dvi , & rinfo - > mon1_EDID ) ) {
radeon_probe_i2c_connector ( rinfo , ddc_crt2 , & rinfo - > mon1_EDID ) ;
ddc_crt2_used = 1 ;
}
if ( rinfo - > mon2_type ! = MT_NONE )
if ( ! radeon_probe_i2c_connector ( rinfo , ddc_vga , & rinfo - > mon2_EDID ) & &
! ddc_crt2_used )
radeon_probe_i2c_connector ( rinfo , ddc_crt2 , & rinfo - > mon2_EDID ) ;
}
# endif /* CONFIG_FB_RADEON_I2C */
if ( rinfo - > mon1_type = = MT_NONE ) {
if ( rinfo - > mon2_type ! = MT_NONE ) {
rinfo - > mon1_type = rinfo - > mon2_type ;
rinfo - > mon1_EDID = rinfo - > mon2_EDID ;
} else {
rinfo - > mon1_type = MT_CRT ;
printk ( KERN_INFO " radeonfb: No valid monitor, assuming CRT on first port \n " ) ;
}
rinfo - > mon2_type = MT_NONE ;
rinfo - > mon2_EDID = NULL ;
}
} else {
/*
* Auto - detecting display type ( well . . . trying to . . . )
*/
RTRACE ( " Starting monitor auto detection... \n " ) ;
# if DEBUG && defined(CONFIG_FB_RADEON_I2C)
{
u8 * EDIDs [ 4 ] = { NULL , NULL , NULL , NULL } ;
int mon_types [ 4 ] = { MT_NONE , MT_NONE , MT_NONE , MT_NONE } ;
int i ;
for ( i = 0 ; i < 4 ; i + + )
mon_types [ i ] = radeon_probe_i2c_connector ( rinfo ,
i + 1 , & EDIDs [ i ] ) ;
}
# endif /* DEBUG */
/*
* Old single head cards
*/
if ( ! rinfo - > has_CRTC2 ) {
# ifdef CONFIG_PPC_OF
if ( rinfo - > mon1_type = = MT_NONE )
rinfo - > mon1_type = radeon_probe_OF_head ( rinfo , 0 ,
& rinfo - > mon1_EDID ) ;
# endif /* CONFIG_PPC_OF */
# ifdef CONFIG_FB_RADEON_I2C
if ( rinfo - > mon1_type = = MT_NONE )
rinfo - > mon1_type =
radeon_probe_i2c_connector ( rinfo , ddc_dvi ,
& rinfo - > mon1_EDID ) ;
if ( rinfo - > mon1_type = = MT_NONE )
rinfo - > mon1_type =
radeon_probe_i2c_connector ( rinfo , ddc_vga ,
& rinfo - > mon1_EDID ) ;
if ( rinfo - > mon1_type = = MT_NONE )
rinfo - > mon1_type =
radeon_probe_i2c_connector ( rinfo , ddc_crt2 ,
& rinfo - > mon1_EDID ) ;
# endif /* CONFIG_FB_RADEON_I2C */
if ( rinfo - > mon1_type = = MT_NONE )
rinfo - > mon1_type = MT_CRT ;
goto bail ;
}
/*
* Check for cards with reversed DACs or TMDS controllers using BIOS
*/
if ( rinfo - > bios_seg & &
( tmp = BIOS_IN16 ( rinfo - > fp_bios_start + 0x50 ) ) ) {
for ( i = 1 ; i < 4 ; i + + ) {
unsigned int tmp0 ;
if ( ! BIOS_IN8 ( tmp + i * 2 ) & & i > 1 )
break ;
tmp0 = BIOS_IN16 ( tmp + i * 2 ) ;
if ( ( ! ( tmp0 & 0x01 ) ) & & ( ( ( tmp0 > > 8 ) & 0x0f ) = = ddc_dvi ) ) {
rinfo - > reversed_DAC = 1 ;
printk ( KERN_INFO " radeonfb: Reversed DACs detected \n " ) ;
}
if ( ( ( ( tmp0 > > 8 ) & 0x0f ) = = ddc_dvi ) & & ( ( tmp0 > > 4 ) & 0x01 ) ) {
rinfo - > reversed_TMDS = 1 ;
printk ( KERN_INFO " radeonfb: Reversed TMDS detected \n " ) ;
}
}
}
/*
* Probe primary head ( DVI or laptop internal panel )
*/
# ifdef CONFIG_PPC_OF
if ( rinfo - > mon1_type = = MT_NONE )
rinfo - > mon1_type = radeon_probe_OF_head ( rinfo , 0 ,
& rinfo - > mon1_EDID ) ;
# endif /* CONFIG_PPC_OF */
# ifdef CONFIG_FB_RADEON_I2C
if ( rinfo - > mon1_type = = MT_NONE )
rinfo - > mon1_type = radeon_probe_i2c_connector ( rinfo , ddc_dvi ,
& rinfo - > mon1_EDID ) ;
if ( rinfo - > mon1_type = = MT_NONE ) {
rinfo - > mon1_type = radeon_probe_i2c_connector ( rinfo , ddc_crt2 ,
& rinfo - > mon1_EDID ) ;
if ( rinfo - > mon1_type ! = MT_NONE )
ddc_crt2_used = 1 ;
}
# endif /* CONFIG_FB_RADEON_I2C */
if ( rinfo - > mon1_type = = MT_NONE & & rinfo - > is_mobility & &
( ( rinfo - > bios_seg & & ( INREG ( BIOS_4_SCRATCH ) & 4 ) )
| | ( INREG ( LVDS_GEN_CNTL ) & LVDS_ON ) ) ) {
rinfo - > mon1_type = MT_LCD ;
printk ( " Non-DDC laptop panel detected \n " ) ;
}
if ( rinfo - > mon1_type = = MT_NONE )
rinfo - > mon1_type = radeon_crt_is_connected ( rinfo , rinfo - > reversed_DAC ) ;
/*
* Probe secondary head ( mostly VGA , can be DVI )
*/
# ifdef CONFIG_PPC_OF
if ( rinfo - > mon2_type = = MT_NONE )
rinfo - > mon2_type = radeon_probe_OF_head ( rinfo , 1 ,
& rinfo - > mon2_EDID ) ;
# endif /* CONFIG_PPC_OF */
# ifdef CONFIG_FB_RADEON_I2C
if ( rinfo - > mon2_type = = MT_NONE )
rinfo - > mon2_type = radeon_probe_i2c_connector ( rinfo , ddc_vga ,
& rinfo - > mon2_EDID ) ;
if ( rinfo - > mon2_type = = MT_NONE & & ! ddc_crt2_used )
rinfo - > mon2_type = radeon_probe_i2c_connector ( rinfo , ddc_crt2 ,
& rinfo - > mon2_EDID ) ;
# endif /* CONFIG_FB_RADEON_I2C */
if ( rinfo - > mon2_type = = MT_NONE )
rinfo - > mon2_type = radeon_crt_is_connected ( rinfo , ! rinfo - > reversed_DAC ) ;
/*
* If we only detected port 2 , we swap them , if none detected ,
* assume CRT ( maybe fallback to old BIOS_SCRATCH stuff ? or look
* at FP registers ? )
*/
if ( rinfo - > mon1_type = = MT_NONE ) {
if ( rinfo - > mon2_type ! = MT_NONE ) {
rinfo - > mon1_type = rinfo - > mon2_type ;
rinfo - > mon1_EDID = rinfo - > mon2_EDID ;
} else
rinfo - > mon1_type = MT_CRT ;
rinfo - > mon2_type = MT_NONE ;
rinfo - > mon2_EDID = NULL ;
}
/*
* Deal with reversed TMDS
*/
if ( rinfo - > reversed_TMDS ) {
/* Always keep internal TMDS as primary head */
if ( rinfo - > mon1_type = = MT_DFP | | rinfo - > mon2_type = = MT_DFP ) {
int tmp_type = rinfo - > mon1_type ;
u8 * tmp_EDID = rinfo - > mon1_EDID ;
rinfo - > mon1_type = rinfo - > mon2_type ;
rinfo - > mon1_EDID = rinfo - > mon2_EDID ;
rinfo - > mon2_type = tmp_type ;
rinfo - > mon2_EDID = tmp_EDID ;
if ( rinfo - > mon1_type = = MT_CRT | | rinfo - > mon2_type = = MT_CRT )
rinfo - > reversed_DAC ^ = 1 ;
}
}
}
if ( ignore_edid ) {
kfree ( rinfo - > mon1_EDID ) ;
rinfo - > mon1_EDID = NULL ;
kfree ( rinfo - > mon2_EDID ) ;
rinfo - > mon2_EDID = NULL ;
}
bail :
printk ( KERN_INFO " radeonfb: Monitor 1 type %s found \n " ,
radeon_get_mon_name ( rinfo - > mon1_type ) ) ;
if ( rinfo - > mon1_EDID )
printk ( KERN_INFO " radeonfb: EDID probed \n " ) ;
if ( ! rinfo - > has_CRTC2 )
return ;
printk ( KERN_INFO " radeonfb: Monitor 2 type %s found \n " ,
radeon_get_mon_name ( rinfo - > mon2_type ) ) ;
if ( rinfo - > mon2_EDID )
printk ( KERN_INFO " radeonfb: EDID probed \n " ) ;
}
/*
* This functions applyes any arch / model / machine specific fixups
* to the panel info . It may eventually alter EDID block as
* well or whatever is specific to a given model and not probed
* properly by the default code
*/
static void radeon_fixup_panel_info ( struct radeonfb_info * rinfo )
{
# ifdef CONFIG_PPC_OF
/*
* LCD Flat panels should use fixed dividers , we enfore that on
* PPC only for now . . .
*/
if ( ! rinfo - > panel_info . use_bios_dividers & & rinfo - > mon1_type = = MT_LCD
& & rinfo - > is_mobility ) {
int ppll_div_sel ;
u32 ppll_divn ;
ppll_div_sel = INREG8 ( CLOCK_CNTL_INDEX + 1 ) & 0x3 ;
radeon_pll_errata_after_index ( rinfo ) ;
ppll_divn = INPLL ( PPLL_DIV_0 + ppll_div_sel ) ;
rinfo - > panel_info . ref_divider = rinfo - > pll . ref_div ;
rinfo - > panel_info . fbk_divider = ppll_divn & 0x7ff ;
rinfo - > panel_info . post_divider = ( ppll_divn > > 16 ) & 0x7 ;
rinfo - > panel_info . use_bios_dividers = 1 ;
printk ( KERN_DEBUG " radeonfb: Using Firmware dividers 0x%08x "
" from PPLL %d \n " ,
rinfo - > panel_info . fbk_divider |
( rinfo - > panel_info . post_divider < < 16 ) ,
ppll_div_sel ) ;
}
# endif /* CONFIG_PPC_OF */
}
/*
* Fill up panel infos from a mode definition , either returned by the EDID
* or from the default mode when we can ' t do any better
*/
static void radeon_var_to_panel_info ( struct radeonfb_info * rinfo , struct fb_var_screeninfo * var )
{
rinfo - > panel_info . xres = var - > xres ;
rinfo - > panel_info . yres = var - > yres ;
rinfo - > panel_info . clock = 100000000 / var - > pixclock ;
rinfo - > panel_info . hOver_plus = var - > right_margin ;
rinfo - > panel_info . hSync_width = var - > hsync_len ;
rinfo - > panel_info . hblank = var - > left_margin +
( var - > right_margin + var - > hsync_len ) ;
rinfo - > panel_info . vOver_plus = var - > lower_margin ;
rinfo - > panel_info . vSync_width = var - > vsync_len ;
rinfo - > panel_info . vblank = var - > upper_margin +
( var - > lower_margin + var - > vsync_len ) ;
rinfo - > panel_info . hAct_high =
( var - > sync & FB_SYNC_HOR_HIGH_ACT ) ! = 0 ;
rinfo - > panel_info . vAct_high =
( var - > sync & FB_SYNC_VERT_HIGH_ACT ) ! = 0 ;
rinfo - > panel_info . valid = 1 ;
/* We use a default of 200ms for the panel power delay,
* I need to have a real schedule ( ) instead of mdelay ' s in the panel code .
* we might be possible to figure out a better power delay either from
* MacOS OF tree or from the EDID block ( proprietary extensions ? )
*/
rinfo - > panel_info . pwr_delay = 200 ;
}
static void radeon_videomode_to_var ( struct fb_var_screeninfo * var ,
const struct fb_videomode * mode )
{
var - > xres = mode - > xres ;
var - > yres = mode - > yres ;
var - > xres_virtual = mode - > xres ;
var - > yres_virtual = mode - > yres ;
var - > xoffset = 0 ;
var - > yoffset = 0 ;
var - > pixclock = mode - > pixclock ;
var - > left_margin = mode - > left_margin ;
var - > right_margin = mode - > right_margin ;
var - > upper_margin = mode - > upper_margin ;
var - > lower_margin = mode - > lower_margin ;
var - > hsync_len = mode - > hsync_len ;
var - > vsync_len = mode - > vsync_len ;
var - > sync = mode - > sync ;
var - > vmode = mode - > vmode ;
}
/*
* Build the modedb for head 1 ( head 2 will come later ) , check panel infos
* from either BIOS or EDID , and pick up the default mode
*/
void __devinit radeon_check_modes ( struct radeonfb_info * rinfo , const char * mode_option )
{
struct fb_info * info = rinfo - > info ;
int has_default_mode = 0 ;
/*
* Fill default var first
*/
info - > var = radeonfb_default_var ;
INIT_LIST_HEAD ( & info - > modelist ) ;
/*
* First check out what BIOS has to say
*/
if ( rinfo - > mon1_type = = MT_LCD )
radeon_get_panel_info_BIOS ( rinfo ) ;
/*
* Parse EDID detailed timings and deduce panel infos if any . Right now
* we only deal with first entry returned by parse_EDID , we may do better
* some day . . .
*/
if ( ! rinfo - > panel_info . use_bios_dividers & & rinfo - > mon1_type ! = MT_CRT
& & rinfo - > mon1_EDID ) {
struct fb_var_screeninfo var ;
RTRACE ( " Parsing EDID data for panel info \n " ) ;
if ( fb_parse_edid ( rinfo - > mon1_EDID , & var ) = = 0 ) {
if ( var . xres > = rinfo - > panel_info . xres & &
var . yres > = rinfo - > panel_info . yres )
radeon_var_to_panel_info ( rinfo , & var ) ;
}
}
/*
* Do any additional platform / arch fixups to the panel infos
*/
radeon_fixup_panel_info ( rinfo ) ;
/*
* If we have some valid panel infos , we setup the default mode based on
* those
*/
if ( rinfo - > mon1_type ! = MT_CRT & & rinfo - > panel_info . valid ) {
struct fb_var_screeninfo * var = & info - > var ;
RTRACE ( " Setting up default mode based on panel info \n " ) ;
var - > xres = rinfo - > panel_info . xres ;
var - > yres = rinfo - > panel_info . yres ;
var - > xres_virtual = rinfo - > panel_info . xres ;
var - > yres_virtual = rinfo - > panel_info . yres ;
var - > xoffset = var - > yoffset = 0 ;
var - > bits_per_pixel = 8 ;
var - > pixclock = 100000000 / rinfo - > panel_info . clock ;
var - > left_margin = ( rinfo - > panel_info . hblank - rinfo - > panel_info . hOver_plus
- rinfo - > panel_info . hSync_width ) ;
var - > right_margin = rinfo - > panel_info . hOver_plus ;
var - > upper_margin = ( rinfo - > panel_info . vblank - rinfo - > panel_info . vOver_plus
- rinfo - > panel_info . vSync_width ) ;
var - > lower_margin = rinfo - > panel_info . vOver_plus ;
var - > hsync_len = rinfo - > panel_info . hSync_width ;
var - > vsync_len = rinfo - > panel_info . vSync_width ;
var - > sync = 0 ;
if ( rinfo - > panel_info . hAct_high )
var - > sync | = FB_SYNC_HOR_HIGH_ACT ;
if ( rinfo - > panel_info . vAct_high )
var - > sync | = FB_SYNC_VERT_HIGH_ACT ;
var - > vmode = 0 ;
has_default_mode = 1 ;
}
/*
* Now build modedb from EDID
*/
if ( rinfo - > mon1_EDID ) {
fb_edid_to_monspecs ( rinfo - > mon1_EDID , & info - > monspecs ) ;
fb_videomode_to_modelist ( info - > monspecs . modedb ,
info - > monspecs . modedb_len ,
& info - > modelist ) ;
rinfo - > mon1_modedb = info - > monspecs . modedb ;
rinfo - > mon1_dbsize = info - > monspecs . modedb_len ;
}
/*
* Finally , if we don ' t have panel infos we need to figure some ( or
* we try to read it from card ) , we try to pick a default mode
* and create some panel infos . Whatever . . .
*/
if ( rinfo - > mon1_type ! = MT_CRT & & ! rinfo - > panel_info . valid ) {
struct fb_videomode * modedb ;
int dbsize ;
char modename [ 32 ] ;
RTRACE ( " Guessing panel info... \n " ) ;
if ( rinfo - > panel_info . xres = = 0 | | rinfo - > panel_info . yres = = 0 ) {
u32 tmp = INREG ( FP_HORZ_STRETCH ) & HORZ_PANEL_SIZE ;
rinfo - > panel_info . xres = ( ( tmp > > HORZ_PANEL_SHIFT ) + 1 ) * 8 ;
tmp = INREG ( FP_VERT_STRETCH ) & VERT_PANEL_SIZE ;
rinfo - > panel_info . yres = ( tmp > > VERT_PANEL_SHIFT ) + 1 ;
}
if ( rinfo - > panel_info . xres = = 0 | | rinfo - > panel_info . yres = = 0 ) {
printk ( KERN_WARNING " radeonfb: Can't find panel size, going back to CRT \n " ) ;
rinfo - > mon1_type = MT_CRT ;
goto pickup_default ;
}
printk ( KERN_WARNING " radeonfb: Assuming panel size %dx%d \n " ,
rinfo - > panel_info . xres , rinfo - > panel_info . yres ) ;
modedb = rinfo - > mon1_modedb ;
dbsize = rinfo - > mon1_dbsize ;
snprintf ( modename , 31 , " %dx%d " , rinfo - > panel_info . xres , rinfo - > panel_info . yres ) ;
if ( fb_find_mode ( & info - > var , info , modename ,
modedb , dbsize , NULL , 8 ) = = 0 ) {
printk ( KERN_WARNING " radeonfb: Can't find mode for panel size, going back to CRT \n " ) ;
rinfo - > mon1_type = MT_CRT ;
goto pickup_default ;
}
has_default_mode = 1 ;
radeon_var_to_panel_info ( rinfo , & info - > var ) ;
}
pickup_default :
/*
* Apply passed - in mode option if any
*/
if ( mode_option ) {
if ( fb_find_mode ( & info - > var , info , mode_option ,
info - > monspecs . modedb ,
info - > monspecs . modedb_len , NULL , 8 ) ! = 0 )
has_default_mode = 1 ;
}
/*
* Still no mode , let ' s pick up a default from the db
*/
if ( ! has_default_mode & & info - > monspecs . modedb ! = NULL ) {
struct fb_monspecs * specs = & info - > monspecs ;
struct fb_videomode * modedb = NULL ;
/* get preferred timing */
if ( specs - > misc & FB_MISC_1ST_DETAIL ) {
int i ;
for ( i = 0 ; i < specs - > modedb_len ; i + + ) {
if ( specs - > modedb [ i ] . flag & FB_MODE_IS_FIRST ) {
modedb = & specs - > modedb [ i ] ;
break ;
}
}
} else {
/* otherwise, get first mode in database */
modedb = & specs - > modedb [ 0 ] ;
}
if ( modedb ! = NULL ) {
info - > var . bits_per_pixel = 8 ;
radeon_videomode_to_var ( & info - > var , modedb ) ;
has_default_mode = 1 ;
}
}
if ( 1 ) {
struct fb_videomode mode ;
/* Make sure that whatever mode got selected is actually in the
* modelist or the kernel may die
*/
fb_var_to_videomode ( & mode , & info - > var ) ;
fb_add_videomode ( & mode , & info - > modelist ) ;
}
}
/*
* The code below is used to pick up a mode in check_var and
* set_var . It should be made generic
*/
/*
* This is used when looking for modes . We assign a " distance " value
* to a mode in the modedb depending how " close " it is from what we
* are looking for .
* Currently , we don ' t compare that much , we could do better but
* the current fbcon doesn ' t quite mind ; )
*/
static int radeon_compare_modes ( const struct fb_var_screeninfo * var ,
const struct fb_videomode * mode )
{
int distance = 0 ;
distance = mode - > yres - var - > yres ;
distance + = ( mode - > xres - var - > xres ) / 2 ;
return distance ;
}
/*
* This function is called by check_var , it gets the passed in mode parameter , and
* outputs a valid mode matching the passed - in one as closely as possible .
* We need something better ultimately . Things like fbcon basically pass us out
* current mode with xres / yres hacked , while things like XFree will actually
* produce a full timing that we should respect as much as possible .
*
* This is why I added the FB_ACTIVATE_FIND that is used by fbcon . Without this ,
* we do a simple spec match , that ' s all . With it , we actually look for a mode in
* either our monitor modedb or the vesa one if none
*
*/
int radeon_match_mode ( struct radeonfb_info * rinfo ,
struct fb_var_screeninfo * dest ,
const struct fb_var_screeninfo * src )
{
const struct fb_videomode * db = vesa_modes ;
int i , dbsize = 34 ;
int has_rmx , native_db = 0 ;
int distance = INT_MAX ;
const struct fb_videomode * candidate = NULL ;
/* Start with a copy of the requested mode */
memcpy ( dest , src , sizeof ( struct fb_var_screeninfo ) ) ;
/* Check if we have a modedb built from EDID */
if ( rinfo - > mon1_modedb ) {
db = rinfo - > mon1_modedb ;
dbsize = rinfo - > mon1_dbsize ;
native_db = 1 ;
}
/* Check if we have a scaler allowing any fancy mode */
has_rmx = rinfo - > mon1_type = = MT_LCD | | rinfo - > mon1_type = = MT_DFP ;
/* If we have a scaler and are passed FB_ACTIVATE_TEST or
* FB_ACTIVATE_NOW , just do basic checking and return if the
* mode match
*/
if ( ( src - > activate & FB_ACTIVATE_MASK ) = = FB_ACTIVATE_TEST | |
( src - > activate & FB_ACTIVATE_MASK ) = = FB_ACTIVATE_NOW ) {
/* We don't have an RMX, validate timings. If we don't have
* monspecs , we should be paranoid and not let use go above
* 640 x480 - 60 , but I assume userland knows what it ' s doing here
* ( though I may be proven wrong . . . )
*/
if ( has_rmx = = 0 & & rinfo - > mon1_modedb )
if ( fb_validate_mode ( ( struct fb_var_screeninfo * ) src , rinfo - > info ) )
return - EINVAL ;
return 0 ;
}
/* Now look for a mode in the database */
while ( db ) {
for ( i = 0 ; i < dbsize ; i + + ) {
int d ;
if ( db [ i ] . yres < src - > yres )
continue ;
if ( db [ i ] . xres < src - > xres )
continue ;
d = radeon_compare_modes ( src , & db [ i ] ) ;
/* If the new mode is at least as good as the previous one,
* then it ' s our new candidate
*/
if ( d < distance ) {
candidate = & db [ i ] ;
distance = d ;
}
}
db = NULL ;
/* If we have a scaler, we allow any mode from the database */
if ( native_db & & has_rmx ) {
db = vesa_modes ;
dbsize = 34 ;
native_db = 0 ;
}
}
/* If we have found a match, return it */
if ( candidate ! = NULL ) {
radeon_videomode_to_var ( dest , candidate ) ;
return 0 ;
}
/* If we haven't and don't have a scaler, fail */
if ( ! has_rmx )
return - EINVAL ;
return 0 ;
}