@ -129,6 +129,62 @@ nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp,
return data ;
}
static void
nv50_disp_super_ied_on ( struct nvkm_head * head ,
struct nvkm_ior * ior , int id , u32 khz )
{
struct nvkm_subdev * subdev = & head - > disp - > engine . subdev ;
struct nvkm_bios * bios = subdev - > device - > bios ;
struct nvkm_outp * outp = ior - > asy . outp ;
struct nvbios_ocfg iedtrs ;
struct nvbios_outp iedt ;
u8 ver , hdr , cnt , len , flags = 0x00 ;
u32 data ;
if ( ! outp ) {
IOR_DBG ( ior , " nothing to attach " ) ;
return ;
}
/* Lookup IED table for the device. */
data = nv50_disp_super_iedt ( head , outp , & ver , & hdr , & cnt , & len , & iedt ) ;
if ( ! data )
return ;
/* Lookup IEDT runtime settings for the current configuration. */
if ( ior - > type = = SOR ) {
if ( ior - > asy . proto = = LVDS ) {
if ( head - > asy . or . depth = = 24 )
flags | = 0x02 ;
}
if ( ior - > asy . link = = 3 )
flags | = 0x01 ;
}
data = nvbios_ocfg_match ( bios , data , ior - > asy . proto_evo , flags ,
& ver , & hdr , & cnt , & len , & iedtrs ) ;
if ( ! data ) {
OUTP_DBG ( outp , " missing IEDT RS for %02x:%02x " ,
ior - > asy . proto_evo , flags ) ;
return ;
}
/* Execute the OnInt[23] script for the current frequency. */
data = nvbios_oclk_match ( bios , iedtrs . clkcmp [ id ] , khz ) ;
if ( ! data ) {
OUTP_DBG ( outp , " missing IEDT RSS %d for %02x:%02x %d khz " ,
id , ior - > asy . proto_evo , flags , khz ) ;
return ;
}
nvbios_init ( subdev , data ,
init . outp = & outp - > info ;
init . or = ior - > id ;
init . link = ior - > asy . link ;
init . head = head - > id ;
) ;
}
static void
nv50_disp_super_ied_off ( struct nvkm_head * head , struct nvkm_ior * ior , int id )
{
@ -154,6 +210,20 @@ nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id)
) ;
}
static struct nvkm_ior *
nv50_disp_super_ior_asy ( struct nvkm_head * head )
{
struct nvkm_ior * ior ;
list_for_each_entry ( ior , & head - > disp - > ior , head ) {
if ( ior - > asy . head & ( 1 < < head - > id ) ) {
HEAD_DBG ( head , " to %s " , ior - > name ) ;
return ior ;
}
}
HEAD_DBG ( head , " nothing to attach " ) ;
return NULL ;
}
static struct nvkm_ior *
nv50_disp_super_ior_arm ( struct nvkm_head * head )
{
@ -327,58 +397,40 @@ nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head)
}
static void
nv50_disp_intr_unk20_2_dp ( struct nv50_disp * disp , int head ,
struct dcb_output * outp , u32 pclk )
nv50_disp_super_2_2_dp ( struct nvkm_head * head , struct nvkm_ior * ior )
{
struct nvkm_subdev * subdev = & disp - > base . engine . subdev ;
struct nvkm_device * device = subdev - > device ;
const int link = ! ( outp - > sorconf . link & 1 ) ;
const int or = ffs ( outp - > or ) - 1 ;
const u32 soff = ( or * 0x800 ) ;
const u32 loff = ( link * 0x080 ) + soff ;
const u32 ctrl = nvkm_rd32 ( device , 0x610794 + ( or * 8 ) ) ;
const u32 symbol = 100000 ;
const s32 vactive = nvkm_rd32 ( device , 0x610af8 + ( head * 0x540 ) ) & 0xffff ;
const s32 vblanke = nvkm_rd32 ( device , 0x610ae8 + ( head * 0x540 ) ) & 0xffff ;
const s32 vblanks = nvkm_rd32 ( device , 0x610af0 + ( head * 0x540 ) ) & 0xffff ;
u32 dpctrl = nvkm_rd32 ( device , 0x61c10c + loff ) ;
u32 clksor = nvkm_rd32 ( device , 0x614300 + soff ) ;
struct nvkm_subdev * subdev = & head - > disp - > engine . subdev ;
const u32 khz = head - > asy . hz / 1000 ;
const u32 linkKBps = ior - > dp . bw * 27000 ;
const u32 symbol = 100000 ;
int bestTU = 0 , bestVTUi = 0 , bestVTUf = 0 , bestVTUa = 0 ;
int TU , VTUi , VTUf , VTUa ;
u64 link_data_rate , link_ratio , unk ;
u32 best_diff = 64 * symbol ;
u32 link_nr , link_bw , bits ;
u64 value ;
link_bw = ( clksor & 0x000c0000 ) ? 270000 : 162000 ;
link_nr = hweight32 ( dpctrl & 0x000f0000 ) ;
u64 h , v ;
/* symbols/hblank - algorithm taken from comments in tegra driver */
value = vblanke + vactive - vblanks - 7 ;
value = value * link_bw ;
do_div ( value , pclk ) ;
value = value - ( 3 * ! ! ( dpctrl & 0x00004000 ) ) - ( 12 / link_nr ) ;
nvkm_mask ( device , 0x61c1e8 + soff , 0x0000ffff , value ) ;
h = head - > asy . hblanke + head - > asy . htotal - head - > asy . hblanks - 7 ;
h = h * linkKBps ;
do_div ( h , khz ) ;
h = h - ( 3 * ior - > dp . ef ) - ( 12 / ior - > dp . nr ) ;
/* symbols/vblank - algorithm taken from comments in tegra driver */
value = vblanks - vblanke - 25 ;
value = value * link_bw ;
do_div ( value , pclk ) ;
value = value - ( ( 36 / link_nr ) + 3 ) - 1 ;
nvkm_mask ( device , 0x61c1ec + soff , 0x00ffffff , value ) ;
v = head - > asy . vblanks - head - > asy . vblanke - 25 ;
v = v * linkKBps ;
do_div ( v , khz ) ;
v = v - ( ( 36 / ior - > dp . nr ) + 3 ) - 1 ;
/* watermark / activesym */
if ( ( ctrl & 0xf0000 ) = = 0x60000 ) bits = 30 ;
else if ( ( ctrl & 0xf0000 ) = = 0x50000 ) bits = 24 ;
else bits = 18 ;
ior - > func - > dp . audio_sym ( ior , head - > id , h , v ) ;
link_data_rate = ( pclk * bits / 8 ) / link_nr ;
/* watermark / activesym */
link_data_rate = ( khz * head - > asy . or . depth / 8 ) / ior - > dp . nr ;
/* calculate ratio of packed data rate to link symbol rate */
link_ratio = link_data_rate * symbol ;
do_div ( link_ratio , link_bw ) ;
do_div ( link_ratio , linkKBps ) ;
for ( TU = 64 ; TU > = 32 ; TU - - ) {
for ( TU = 64 ; ior - > func - > dp . activesym & & TU > = 32 ; TU - - ) {
/* calculate average number of valid symbols in each TU */
u32 tu_valid = link_ratio * TU ;
u32 calc , diff ;
@ -429,9 +481,15 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
}
}
if ( ! bestTU ) {
nvkm_error ( subdev , " unable to find suitable dp config \n " ) ;
return ;
if ( ior - > func - > dp . activesym ) {
if ( ! bestTU ) {
nvkm_error ( subdev , " unable to determine dp config \n " ) ;
return ;
}
ior - > func - > dp . activesym ( ior , head - > id , bestTU ,
bestVTUa , bestVTUf , bestVTUi ) ;
} else {
bestTU = 64 ;
}
/* XXX close to vbios numbers, but not right */
@ -441,102 +499,61 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head,
do_div ( unk , symbol ) ;
unk + = 6 ;
nvkm_mask ( device , 0x61c10c + loff , 0x000001fc , bestTU < < 2 ) ;
nvkm_mask ( device , 0x61c128 + loff , 0x010f7f3f , bestVTUa < < 24 |
bestVTUf < < 16 |
bestVTUi < < 8 | unk ) ;
ior - > func - > dp . watermark ( ior , head - > id , unk ) ;
}
static void
nv50_disp_intr_unk20 _2 ( struct nv50_disp * disp , int head )
void
nv50_disp_super_2 _2 ( struct nv50_disp * disp , struct nvkm_head * head )
{
struct nvkm_device * device = disp - > base . engine . subdev . device ;
struct nvkm_output * outp ;
u32 pclk = nvkm_rd32 ( device , 0x610ad0 + ( head * 0x540 ) ) & 0x3fffff ;
u32 hval , hreg = 0x614200 + ( head * 0x800 ) ;
u32 oval , oreg ;
u32 mask , conf ;
const u32 khz = head - > asy . hz / 1000 ;
struct nvkm_outp * outp ;
struct nvkm_ior * ior ;
outp = exec_clkcmp ( disp , head , 0xff , pclk , & conf ) ;
if ( ! outp )
/* Determine which OR, if any, we're attaching from the head. */
HEAD_DBG ( head , " supervisor 2.2 " ) ;
ior = nv50_disp_super_ior_asy ( head ) ;
if ( ! ior )
return ;
/* we allow both encoder attach and detach operations to occur
* within a single supervisor ( ie . modeset ) sequence . the
* encoder detach scripts quite often switch off power to the
* lanes , which requires the link to be re - trained .
*
* this is not generally an issue as the sink " must " ( heh )
* signal an irq when it ' s lost sync so the driver can
* re - train .
/* For some reason, NVIDIA decided not to:
*
* however , on some boards , if one does not configure at least
* the gpu side of the link * before * attaching , then various
* things can go horribly wrong ( PDISP disappearing from mmio ,
* third supervisor never happens , etc ) .
* A ) Give dual - link LVDS a separate EVO protocol , like for TMDS .
* and
* B ) Use SetControlOutputResource . PixelDepth on LVDS .
*
* the solution is simply to retrain here , if necessary . last
* i checked , the binary driver userspace does not appear to
* trigger this situation ( it forces an UPDATE between steps ) .
* Override the values we usually read from HW with the same
* data we pass though an ioctl instead .
*/
if ( outp - > info . type = = DCB_OUTPUT_DP ) {
u32 soff = ( ffs ( outp - > info . or ) - 1 ) * 0x08 ;
u32 ctrl , datarate ;
if ( outp - > info . location = = 0 ) {
ctrl = nvkm_rd32 ( device , 0x610794 + soff ) ;
soff = 1 ;
} else {
ctrl = nvkm_rd32 ( device , 0x610b80 + soff ) ;
soff = 2 ;
}
switch ( ( ctrl & 0x000f0000 ) > > 16 ) {
case 6 : datarate = pclk * 30 ; break ;
case 5 : datarate = pclk * 24 ; break ;
case 2 :
default :
datarate = pclk * 18 ;
break ;
}
if ( nvkm_output_dp_train ( outp , datarate / soff ) )
OUTP_ERR ( outp , " link not trained before attach " ) ;
if ( ior - > type = = SOR & & ior - > asy . proto = = LVDS ) {
head - > asy . or . depth = ( disp - > sor . lvdsconf & 0x0200 ) ? 24 : 18 ;
ior - > asy . link = ( disp - > sor . lvdsconf & 0x0100 ) ? 3 : 1 ;
}
exec_clkcmp ( disp , head , 0 , pclk , & conf ) ;
/* Handle any link training, etc. */
if ( ( outp = ior - > asy . outp ) & & outp - > func - > acquire )
outp - > func - > acquire ( outp ) ;
if ( ! outp - > info . location & & outp - > info . type = = DCB_OUTPUT_ANALOG ) {
oreg = 0x614280 + ( ffs ( outp - > info . or ) - 1 ) * 0x800 ;
oval = 0x00000000 ;
hval = 0x00000000 ;
mask = 0xffffffff ;
} else
if ( ! outp - > info . location ) {
if ( outp - > info . type = = DCB_OUTPUT_DP )
nv50_disp_intr_unk20_2_dp ( disp , head , & outp - > info , pclk ) ;
oreg = 0x614300 + ( ffs ( outp - > info . or ) - 1 ) * 0x800 ;
oval = ( conf & 0x0100 ) ? 0x00000101 : 0x00000000 ;
hval = 0x00000000 ;
mask = 0x00000707 ;
} else {
oreg = 0x614380 + ( ffs ( outp - > info . or ) - 1 ) * 0x800 ;
oval = 0x00000001 ;
hval = 0x00000001 ;
mask = 0x00000707 ;
}
/* Execute OnInt2 IED script. */
nv50_disp_super_ied_on ( head , ior , 0 , khz ) ;
/* Program RG clock divider. */
head - > func - > rgclk ( head , ior - > asy . rgdiv ) ;
nvkm_mask ( device , hreg , 0x0000000f , hval ) ;
nvkm_mask ( device , oreg , mask , oval ) ;
/* Mode-specific internal DP configuration. */
if ( ior - > type = = SOR & & ior - > asy . proto = = DP )
nv50_disp_super_2_2_dp ( head , ior ) ;
nv50_disp_dptmds_war_2 ( disp , & outp - > info ) ;
/* OR-specific handling. */
ior - > func - > clock ( ior ) ;
if ( ior - > func - > war_2 )
ior - > func - > war_2 ( ior ) ;
}
void
nv50_disp_super_2_1 ( struct nv50_disp * disp , struct nvkm_head * head )
{
struct nvkm_devinit * devinit = disp - > base . engine . subdev . device - > devinit ;
u32 khz = head - > asy . hz / 1000 ;
const u32 khz = head - > asy . hz / 1000 ;
HEAD_DBG ( head , " supervisor 2.1 - %d khz " , khz ) ;
if ( khz )
nvkm_devinit_pll_set ( devinit , PLL_VPLL0 + head - > id , khz ) ;
@ -636,7 +653,7 @@ nv50_disp_super(struct work_struct *work)
list_for_each_entry ( head , & disp - > base . head , head ) {
if ( ! ( super & ( 0x00000080 < < head - > id ) ) )
continue ;
nv50_disp_intr_unk20 _2 ( disp , head - > i d ) ;
nv50_disp_super_2 _2 ( disp , head ) ;
}
} else
if ( disp - > super & 0x00000040 ) {