2010-09-16 15:39:49 +10:00
/*
* Copyright 2010 Red Hat Inc .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) BE LIABLE FOR ANY CLAIM , DAMAGES OR
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE .
*
* Authors : Ben Skeggs
*/
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
2010-09-16 15:39:49 +10:00
2012-07-31 16:16:21 +10:00
# include "nouveau_drm.h"
# include "nouveau_reg.h"
2010-09-16 15:39:49 +10:00
# include "nouveau_pm.h"
2012-01-16 22:34:03 +10:00
static u8 *
nouveau_perf_table ( struct drm_device * dev , u8 * ver )
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nvbios * bios = & drm - > vbios ;
2012-01-16 22:34:03 +10:00
struct bit_entry P ;
if ( ! bit_table ( dev , ' P ' , & P ) & & P . version & & P . version < = 2 ) {
u8 * perf = ROMPTR ( dev , P . data [ 0 ] ) ;
if ( perf ) {
* ver = perf [ 0 ] ;
return perf ;
}
}
if ( bios - > type = = NVBIOS_BMP ) {
if ( bios - > data [ bios - > offset + 6 ] > = 0x25 ) {
u8 * perf = ROMPTR ( dev , bios - > data [ bios - > offset + 0x94 ] ) ;
if ( perf ) {
* ver = perf [ 1 ] ;
return perf ;
}
}
}
return NULL ;
}
static u8 *
nouveau_perf_entry ( struct drm_device * dev , int idx ,
u8 * ver , u8 * hdr , u8 * cnt , u8 * len )
{
u8 * perf = nouveau_perf_table ( dev , ver ) ;
if ( perf ) {
if ( * ver > = 0x12 & & * ver < 0x20 & & idx < perf [ 2 ] ) {
* hdr = perf [ 3 ] ;
* cnt = 0 ;
* len = 0 ;
return perf + perf [ 0 ] + idx * perf [ 3 ] ;
} else
if ( * ver > = 0x20 & & * ver < 0x40 & & idx < perf [ 2 ] ) {
* hdr = perf [ 3 ] ;
* cnt = perf [ 4 ] ;
* len = perf [ 5 ] ;
return perf + perf [ 1 ] + idx * ( * hdr + ( * cnt * * len ) ) ;
} else
if ( * ver > = 0x40 & & * ver < 0x41 & & idx < perf [ 5 ] ) {
* hdr = perf [ 2 ] ;
* cnt = perf [ 4 ] ;
* len = perf [ 3 ] ;
return perf + perf [ 1 ] + idx * ( * hdr + ( * cnt * * len ) ) ;
}
}
return NULL ;
}
2012-01-27 10:53:17 +10:00
u8 *
2012-01-16 22:34:03 +10:00
nouveau_perf_rammap ( struct drm_device * dev , u32 freq ,
u8 * ver , u8 * hdr , u8 * cnt , u8 * len )
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2012-01-16 22:34:03 +10:00
struct bit_entry P ;
2012-01-17 21:10:58 +10:00
u8 * perf , i = 0 ;
2012-01-16 22:34:03 +10:00
if ( ! bit_table ( dev , ' P ' , & P ) & & P . version = = 2 ) {
u8 * rammap = ROMPTR ( dev , P . data [ 4 ] ) ;
if ( rammap ) {
u8 * ramcfg = rammap + rammap [ 1 ] ;
* ver = rammap [ 0 ] ;
* hdr = rammap [ 2 ] ;
* cnt = rammap [ 4 ] ;
* len = rammap [ 3 ] ;
freq / = 1000 ;
for ( i = 0 ; i < rammap [ 5 ] ; i + + ) {
if ( freq > = ROM16 ( ramcfg [ 0 ] ) & &
freq < = ROM16 ( ramcfg [ 2 ] ) )
return ramcfg ;
ramcfg + = * hdr + ( * cnt * * len ) ;
}
}
return NULL ;
}
2012-07-31 16:16:21 +10:00
if ( nv_device ( drm - > device ) - > chipset = = 0x49 | |
nv_device ( drm - > device ) - > chipset = = 0x4b )
2012-01-16 22:34:03 +10:00
freq / = 2 ;
while ( ( perf = nouveau_perf_entry ( dev , i + + , ver , hdr , cnt , len ) ) ) {
if ( * ver > = 0x20 & & * ver < 0x25 ) {
if ( perf [ 0 ] ! = 0xff & & freq < = ROM16 ( perf [ 11 ] ) * 1000 )
break ;
} else
if ( * ver > = 0x25 & & * ver < 0x40 ) {
if ( perf [ 0 ] ! = 0xff & & freq < = ROM16 ( perf [ 12 ] ) * 1000 )
break ;
}
}
if ( perf ) {
u8 * ramcfg = perf + * hdr ;
* ver = 0x00 ;
* hdr = 0 ;
return ramcfg ;
}
return NULL ;
}
2012-01-24 11:26:40 +10:00
u8 *
2012-01-16 22:34:03 +10:00
nouveau_perf_ramcfg ( struct drm_device * dev , u32 freq , u8 * ver , u8 * len )
{
2012-07-31 16:16:21 +10:00
struct nouveau_device * device = nouveau_dev ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nvbios * bios = & drm - > vbios ;
2012-01-16 22:34:03 +10:00
u8 strap , hdr , cnt ;
u8 * rammap ;
2012-07-31 16:16:21 +10:00
strap = ( nv_rd32 ( device , 0x101000 ) & 0x0000003c ) > > 2 ;
2012-01-16 22:34:03 +10:00
if ( bios - > ram_restrict_tbl_ptr )
strap = bios - > data [ bios - > ram_restrict_tbl_ptr + strap ] ;
rammap = nouveau_perf_rammap ( dev , freq , ver , & hdr , & cnt , len ) ;
if ( rammap & & strap < cnt )
return rammap + hdr + ( strap * * len ) ;
return NULL ;
}
2012-01-17 21:10:58 +10:00
u8 *
2012-01-16 22:34:03 +10:00
nouveau_perf_timing ( struct drm_device * dev , u32 freq , u8 * ver , u8 * len )
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nvbios * bios = & drm - > vbios ;
2012-01-16 22:34:03 +10:00
struct bit_entry P ;
u8 * perf , * timing = NULL ;
u8 i = 0 , hdr , cnt ;
if ( bios - > type = = NVBIOS_BMP ) {
while ( ( perf = nouveau_perf_entry ( dev , i + + , ver , & hdr , & cnt ,
len ) ) & & * ver = = 0x15 ) {
if ( freq < = ROM32 ( perf [ 5 ] ) * 20 ) {
* ver = 0x00 ;
* len = 14 ;
return perf + 41 ;
}
}
return NULL ;
}
if ( ! bit_table ( dev , ' P ' , & P ) ) {
if ( P . version = = 1 )
timing = ROMPTR ( dev , P . data [ 4 ] ) ;
else
if ( P . version = = 2 )
timing = ROMPTR ( dev , P . data [ 8 ] ) ;
}
if ( timing & & timing [ 0 ] = = 0x10 ) {
u8 * ramcfg = nouveau_perf_ramcfg ( dev , freq , ver , len ) ;
if ( ramcfg & & ramcfg [ 1 ] < timing [ 2 ] ) {
* ver = timing [ 0 ] ;
* len = timing [ 3 ] ;
return timing + timing [ 1 ] + ( ramcfg [ 1 ] * timing [ 3 ] ) ;
}
}
return NULL ;
}
2010-09-20 16:18:28 +02:00
static void
legacy_perf_init ( 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 ) ;
struct nvbios * bios = & drm - > vbios ;
struct nouveau_pm * pm = nouveau_pm ( dev ) ;
2010-09-20 16:18:28 +02:00
char * perf , * entry , * bmp = & bios - > data [ bios - > offset ] ;
int headerlen , use_straps ;
if ( bmp [ 5 ] < 0x5 | | bmp [ 6 ] < 0x14 ) {
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " BMP version too old for perf \n " ) ;
2010-09-20 16:18:28 +02:00
return ;
}
2011-10-12 16:48:48 +10:00
perf = ROMPTR ( dev , bmp [ 0x73 ] ) ;
2010-09-20 16:18:28 +02:00
if ( ! perf ) {
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " No memclock table pointer found. \n " ) ;
2010-09-20 16:18:28 +02:00
return ;
}
switch ( perf [ 0 ] ) {
case 0x12 :
case 0x14 :
case 0x18 :
use_straps = 0 ;
headerlen = 1 ;
break ;
case 0x01 :
use_straps = perf [ 1 ] & 1 ;
headerlen = ( use_straps ? 8 : 2 ) ;
break ;
default :
2012-07-31 16:16:21 +10:00
NV_WARN ( drm , " Unknown memclock table version %x. \n " , perf [ 0 ] ) ;
2010-09-20 16:18:28 +02:00
return ;
}
entry = perf + headerlen ;
if ( use_straps )
2012-07-31 16:16:21 +10:00
entry + = ( nv_rd32 ( device , NV_PEXTDEV_BOOT_0 ) & 0x3c ) > > 1 ;
2010-09-20 16:18:28 +02:00
sprintf ( pm - > perflvl [ 0 ] . name , " performance_level_0 " ) ;
pm - > perflvl [ 0 ] . memory = ROM16 ( entry [ 0 ] ) * 20 ;
pm - > nr_perflvl = 1 ;
}
2011-06-09 16:57:07 +10:00
static void
2012-01-16 22:34:03 +10:00
nouveau_perf_voltage ( struct drm_device * dev , struct nouveau_pm_level * perflvl )
2011-06-09 16:57:07 +10:00
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2012-01-16 22:34:03 +10:00
struct bit_entry P ;
2011-06-09 16:57:07 +10:00
u8 * vmap ;
int id ;
id = perflvl - > volt_min ;
perflvl - > volt_min = 0 ;
2011-06-10 15:33:11 +10:00
/* boards using voltage table version <0x40 store the voltage
* level directly in the perflvl entry as a multiple of 10 mV
2011-06-09 16:57:07 +10:00
*/
2012-07-31 16:16:21 +10:00
if ( drm - > pm - > voltage . version < 0x40 ) {
2011-06-09 16:57:07 +10:00
perflvl - > volt_min = id * 10000 ;
perflvl - > volt_max = perflvl - > volt_min ;
return ;
}
2011-06-10 15:33:11 +10:00
/* on newer ones, the perflvl stores an index into yet another
2011-06-09 16:57:07 +10:00
* vbios table containing a min / max voltage value for the perflvl
*/
2012-01-16 22:34:03 +10:00
if ( bit_table ( dev , ' P ' , & P ) | | P . version ! = 2 | | P . length < 34 ) {
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " where's our volt map table ptr? %d %d \n " ,
2012-01-16 22:34:03 +10:00
P . version , P . length ) ;
2011-06-09 16:57:07 +10:00
return ;
}
2012-01-16 22:34:03 +10:00
vmap = ROMPTR ( dev , P . data [ 32 ] ) ;
2011-06-09 16:57:07 +10:00
if ( ! vmap ) {
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " volt map table pointer invalid \n " ) ;
2011-06-09 16:57:07 +10:00
return ;
}
if ( id < vmap [ 3 ] ) {
vmap + = vmap [ 1 ] + ( vmap [ 2 ] * id ) ;
perflvl - > volt_min = ROM32 ( vmap [ 0 ] ) ;
perflvl - > volt_max = ROM32 ( vmap [ 4 ] ) ;
}
}
2010-09-16 15:39:49 +10:00
void
nouveau_perf_init ( struct drm_device * dev )
{
2012-07-31 16:16:21 +10:00
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
struct nouveau_pm * pm = nouveau_pm ( dev ) ;
struct nvbios * bios = & drm - > vbios ;
2012-01-16 22:34:03 +10:00
u8 * perf , ver , hdr , cnt , len ;
2012-01-18 09:02:28 +10:00
int ret , vid , i = - 1 ;
2010-09-16 15:39:49 +10:00
2012-01-16 22:34:03 +10:00
if ( bios - > type = = NVBIOS_BMP & & bios - > data [ bios - > offset + 6 ] < 0x25 ) {
legacy_perf_init ( dev ) ;
return ;
2011-07-09 21:18:11 +02:00
}
2012-02-09 15:25:25 +10:00
perf = nouveau_perf_table ( dev , & ver ) ;
2012-01-16 22:34:03 +10:00
while ( ( perf = nouveau_perf_entry ( dev , + + i , & ver , & hdr , & cnt , & len ) ) ) {
2010-09-16 15:39:49 +10:00
struct nouveau_pm_level * perflvl = & pm - > perflvl [ pm - > nr_perflvl ] ;
2012-01-16 22:34:03 +10:00
if ( perf [ 0 ] = = 0xff )
2010-09-16 15:39:49 +10:00
continue ;
2012-01-16 22:34:03 +10:00
switch ( ver ) {
2010-09-16 15:39:49 +10:00
case 0x12 :
case 0x13 :
case 0x15 :
2012-01-16 22:34:03 +10:00
perflvl - > fanspeed = perf [ 55 ] ;
if ( hdr > 56 )
perflvl - > volt_min = perf [ 56 ] ;
perflvl - > core = ROM32 ( perf [ 1 ] ) * 10 ;
perflvl - > memory = ROM32 ( perf [ 5 ] ) * 20 ;
2010-09-16 15:39:49 +10:00
break ;
case 0x21 :
case 0x23 :
case 0x24 :
2012-01-16 22:34:03 +10:00
perflvl - > fanspeed = perf [ 4 ] ;
perflvl - > volt_min = perf [ 5 ] ;
perflvl - > shader = ROM16 ( perf [ 6 ] ) * 1000 ;
2011-07-18 16:02:37 +10:00
perflvl - > core = perflvl - > shader ;
2012-01-16 22:34:03 +10:00
perflvl - > core + = ( signed char ) perf [ 8 ] * 1000 ;
2012-07-31 16:16:21 +10:00
if ( nv_device ( drm - > device ) - > chipset = = 0x49 | |
nv_device ( drm - > device ) - > chipset = = 0x4b )
2012-01-16 22:34:03 +10:00
perflvl - > memory = ROM16 ( perf [ 11 ] ) * 1000 ;
2010-09-23 15:34:09 +02:00
else
2012-01-16 22:34:03 +10:00
perflvl - > memory = ROM16 ( perf [ 11 ] ) * 2000 ;
2010-09-16 15:39:49 +10:00
break ;
case 0x25 :
2012-01-16 22:34:03 +10:00
perflvl - > fanspeed = perf [ 4 ] ;
perflvl - > volt_min = perf [ 5 ] ;
perflvl - > core = ROM16 ( perf [ 6 ] ) * 1000 ;
perflvl - > shader = ROM16 ( perf [ 10 ] ) * 1000 ;
perflvl - > memory = ROM16 ( perf [ 12 ] ) * 1000 ;
2010-09-16 15:39:49 +10:00
break ;
case 0x30 :
2012-01-16 22:34:03 +10:00
perflvl - > memscript = ROM16 ( perf [ 2 ] ) ;
2010-09-16 15:39:49 +10:00
case 0x35 :
2012-01-16 22:34:03 +10:00
perflvl - > fanspeed = perf [ 6 ] ;
perflvl - > volt_min = perf [ 7 ] ;
perflvl - > core = ROM16 ( perf [ 8 ] ) * 1000 ;
perflvl - > shader = ROM16 ( perf [ 10 ] ) * 1000 ;
perflvl - > memory = ROM16 ( perf [ 12 ] ) * 1000 ;
perflvl - > vdec = ROM16 ( perf [ 16 ] ) * 1000 ;
perflvl - > dom6 = ROM16 ( perf [ 20 ] ) * 1000 ;
2010-09-16 15:39:49 +10:00
break ;
case 0x40 :
2012-01-16 22:34:03 +10:00
# define subent(n) ((ROM16(perf[hdr + (n) * len]) & 0xfff) * 1000)
2010-09-16 15:39:49 +10:00
perflvl - > fanspeed = 0 ; /*XXX*/
2012-01-16 22:34:03 +10:00
perflvl - > volt_min = perf [ 2 ] ;
2012-07-31 16:16:21 +10:00
if ( nv_device ( drm - > device ) - > card_type = = NV_50 ) {
2011-06-21 15:12:26 +10:00
perflvl - > core = subent ( 0 ) ;
perflvl - > shader = subent ( 1 ) ;
perflvl - > memory = subent ( 2 ) ;
perflvl - > vdec = subent ( 3 ) ;
perflvl - > unka0 = subent ( 4 ) ;
2011-04-20 13:15:02 +10:00
} else {
2011-06-21 15:12:26 +10:00
perflvl - > hub06 = subent ( 0 ) ;
perflvl - > hub01 = subent ( 1 ) ;
perflvl - > copy = subent ( 2 ) ;
perflvl - > shader = subent ( 3 ) ;
perflvl - > rop = subent ( 4 ) ;
perflvl - > memory = subent ( 5 ) ;
perflvl - > vdec = subent ( 6 ) ;
perflvl - > daemon = subent ( 10 ) ;
perflvl - > hub07 = subent ( 11 ) ;
2011-04-20 13:15:02 +10:00
perflvl - > core = perflvl - > shader / 2 ;
}
2010-09-16 15:39:49 +10:00
break ;
}
/* make sure vid is valid */
2012-01-16 22:34:03 +10:00
nouveau_perf_voltage ( dev , perflvl ) ;
2011-06-09 16:57:07 +10:00
if ( pm - > voltage . supported & & perflvl - > volt_min ) {
vid = nouveau_volt_vid_lookup ( dev , perflvl - > volt_min ) ;
2010-09-16 15:39:49 +10:00
if ( vid < 0 ) {
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " perflvl %d, bad vid \n " , i ) ;
2010-09-16 15:39:49 +10:00
continue ;
}
}
2011-04-14 00:46:19 +02:00
/* get the corresponding memory timings */
2012-01-18 09:02:28 +10:00
ret = nouveau_mem_timing_calc ( dev , perflvl - > memory ,
& perflvl - > timing ) ;
if ( ret ) {
2012-07-31 16:16:21 +10:00
NV_DEBUG ( drm , " perflvl %d, bad timing: %d \n " , i , ret ) ;
2012-01-18 09:02:28 +10:00
continue ;
}
2011-04-14 00:46:19 +02:00
2010-09-16 15:39:49 +10:00
snprintf ( perflvl - > name , sizeof ( perflvl - > name ) ,
" performance_level_%d " , i ) ;
perflvl - > id = i ;
2012-01-24 15:59:07 +10:00
snprintf ( perflvl - > profile . name , sizeof ( perflvl - > profile . name ) ,
" %d " , perflvl - > id ) ;
perflvl - > profile . func = & nouveau_pm_static_profile_func ;
list_add_tail ( & perflvl - > profile . head , & pm - > profiles ) ;
2010-09-16 15:39:49 +10:00
pm - > nr_perflvl + + ;
}
}
void
nouveau_perf_fini ( struct drm_device * dev )
{
}