2013-10-15 11:52:56 +10:00
/*
* Copyright 2013 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 < bskeggs @ redhat . com >
*/
2014-08-10 04:10:23 +10:00
# include <nvif/os.h>
# include <nvif/class.h>
2014-08-10 04:10:25 +10:00
# include <nvif/ioctl.h>
2013-10-15 11:52:56 +10:00
2014-08-10 04:10:23 +10:00
# include "nouveau_sysfs.h"
2013-10-15 11:52:56 +10:00
2014-08-10 04:10:30 +10:00
MODULE_PARM_DESC ( pstate , " enable sysfs pstate file, which will be moved in the future " ) ;
2014-08-18 22:43:24 +02:00
int nouveau_pstate ;
2014-08-10 04:10:30 +10:00
module_param_named ( pstate , nouveau_pstate , int , 0400 ) ;
2013-10-15 11:52:56 +10:00
static inline struct drm_device *
drm_device ( struct device * d )
{
2014-02-17 15:17:26 +09:00
return dev_get_drvdata ( d ) ;
2013-10-15 11:52:56 +10:00
}
# define snappendf(p,r,f,a...) do { \
snprintf ( p , r , f , # # a ) ; \
r - = strlen ( p ) ; \
p + = strlen ( p ) ; \
} while ( 0 )
static ssize_t
nouveau_sysfs_pstate_get ( struct device * d , struct device_attribute * a , char * b )
{
struct nouveau_sysfs * sysfs = nouveau_sysfs ( drm_device ( d ) ) ;
2014-08-10 04:10:25 +10:00
struct nvif_control_pstate_info_v0 info = { } ;
2013-10-15 11:52:56 +10:00
size_t cnt = PAGE_SIZE ;
char * buf = b ;
int ret , i ;
2014-08-10 04:10:25 +10:00
ret = nvif_mthd ( & sysfs - > ctrl , NVIF_CONTROL_PSTATE_INFO ,
2014-08-10 04:10:22 +10:00
& info , sizeof ( info ) ) ;
2013-10-15 11:52:56 +10:00
if ( ret )
return ret ;
for ( i = 0 ; i < info . count + 1 ; i + + ) {
const s32 state = i < info . count ? i :
2014-08-10 04:10:25 +10:00
NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT ;
struct nvif_control_pstate_attr_v0 attr = {
2013-10-15 11:52:56 +10:00
. state = state ,
. index = 0 ,
} ;
2014-08-10 04:10:25 +10:00
ret = nvif_mthd ( & sysfs - > ctrl , NVIF_CONTROL_PSTATE_ATTR ,
2014-08-10 04:10:22 +10:00
& attr , sizeof ( attr ) ) ;
2013-10-15 11:52:56 +10:00
if ( ret )
return ret ;
if ( i < info . count )
snappendf ( buf , cnt , " %02x: " , attr . state ) ;
else
2014-06-13 14:17:09 +10:00
snappendf ( buf , cnt , " %s: " , info . pwrsrc = = 0 ? " DC " :
info . pwrsrc = = 1 ? " AC " :
" -- " ) ;
2013-10-15 11:52:56 +10:00
attr . index = 0 ;
do {
attr . state = state ;
2014-08-10 04:10:25 +10:00
ret = nvif_mthd ( & sysfs - > ctrl ,
NVIF_CONTROL_PSTATE_ATTR ,
2014-08-10 04:10:22 +10:00
& attr , sizeof ( attr ) ) ;
2013-10-15 11:52:56 +10:00
if ( ret )
return ret ;
snappendf ( buf , cnt , " %s %d " , attr . name , attr . min ) ;
if ( attr . min ! = attr . max )
snappendf ( buf , cnt , " -%d " , attr . max ) ;
snappendf ( buf , cnt , " %s " , attr . unit ) ;
} while ( attr . index ) ;
2014-06-13 14:17:09 +10:00
if ( state > = 0 ) {
if ( info . ustate_ac = = state )
snappendf ( buf , cnt , " AC " ) ;
if ( info . ustate_dc = = state )
snappendf ( buf , cnt , " DC " ) ;
if ( info . pstate = = state )
snappendf ( buf , cnt , " * " ) ;
} else {
if ( info . ustate_ac < - 1 )
snappendf ( buf , cnt , " AC " ) ;
if ( info . ustate_dc < - 1 )
snappendf ( buf , cnt , " DC " ) ;
}
2013-10-15 11:52:56 +10:00
snappendf ( buf , cnt , " \n " ) ;
}
return strlen ( b ) ;
}
static ssize_t
nouveau_sysfs_pstate_set ( struct device * d , struct device_attribute * a ,
const char * buf , size_t count )
{
struct nouveau_sysfs * sysfs = nouveau_sysfs ( drm_device ( d ) ) ;
2014-08-10 04:10:25 +10:00
struct nvif_control_pstate_user_v0 args = { . pwrsrc = - EINVAL } ;
2013-10-15 11:52:56 +10:00
long value , ret ;
char * tmp ;
if ( ( tmp = strchr ( buf , ' \n ' ) ) )
* tmp = ' \0 ' ;
2014-06-13 14:17:09 +10:00
if ( ! strncasecmp ( buf , " dc: " , 3 ) ) {
args . pwrsrc = 0 ;
buf + = 3 ;
} else
if ( ! strncasecmp ( buf , " ac: " , 3 ) ) {
args . pwrsrc = 1 ;
buf + = 3 ;
}
2013-10-15 11:52:56 +10:00
if ( ! strcasecmp ( buf , " none " ) )
2014-08-10 04:10:25 +10:00
args . ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_UNKNOWN ;
2013-10-15 11:52:56 +10:00
else
if ( ! strcasecmp ( buf , " auto " ) )
2014-08-10 04:10:25 +10:00
args . ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_PERFMON ;
2013-10-15 11:52:56 +10:00
else {
ret = kstrtol ( buf , 16 , & value ) ;
if ( ret )
return ret ;
2014-06-13 14:17:09 +10:00
args . ustate = value ;
2013-10-15 11:52:56 +10:00
}
2014-08-10 04:10:25 +10:00
ret = nvif_mthd ( & sysfs - > ctrl , NVIF_CONTROL_PSTATE_USER ,
2014-08-10 04:10:22 +10:00
& args , sizeof ( args ) ) ;
2013-10-15 11:52:56 +10:00
if ( ret < 0 )
return ret ;
return count ;
}
static DEVICE_ATTR ( pstate , S_IRUGO | S_IWUSR ,
nouveau_sysfs_pstate_get , nouveau_sysfs_pstate_set ) ;
void
nouveau_sysfs_fini ( struct drm_device * dev )
{
struct nouveau_sysfs * sysfs = nouveau_sysfs ( dev ) ;
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2014-08-10 04:10:22 +10:00
struct nvif_device * device = & drm - > device ;
2013-10-15 11:52:56 +10:00
2014-08-10 04:10:30 +10:00
if ( sysfs & & sysfs - > ctrl . priv ) {
2015-01-12 12:33:37 +10:00
device_remove_file ( nv_device_base ( nvxx_device ( device ) ) , & dev_attr_pstate ) ;
2014-08-10 04:10:22 +10:00
nvif_object_fini ( & sysfs - > ctrl ) ;
2013-10-15 11:52:56 +10:00
}
drm - > sysfs = NULL ;
kfree ( sysfs ) ;
}
int
nouveau_sysfs_init ( struct drm_device * dev )
{
struct nouveau_drm * drm = nouveau_drm ( dev ) ;
2014-08-10 04:10:22 +10:00
struct nvif_device * device = & drm - > device ;
2013-10-15 11:52:56 +10:00
struct nouveau_sysfs * sysfs ;
int ret ;
2014-08-10 04:10:30 +10:00
if ( ! nouveau_pstate )
return 0 ;
2013-10-15 11:52:56 +10:00
sysfs = drm - > sysfs = kzalloc ( sizeof ( * sysfs ) , GFP_KERNEL ) ;
if ( ! sysfs )
return - ENOMEM ;
2015-08-20 14:54:15 +10:00
ret = nvif_object_init ( & device - > object , NVDRM_CONTROL ,
2014-08-10 04:10:25 +10:00
NVIF_IOCTL_NEW_V0_CONTROL , NULL , 0 ,
2015-08-20 14:54:15 +10:00
& sysfs - > ctrl ) ;
2013-10-15 11:52:56 +10:00
if ( ret = = 0 )
2015-01-12 12:33:37 +10:00
device_create_file ( nv_device_base ( nvxx_device ( device ) ) , & dev_attr_pstate ) ;
2013-10-15 11:52:56 +10:00
return 0 ;
}