Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
b9ed919f
K
Kernel
项目概览
openeuler
/
Kernel
1 年多 前同步成功
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
b9ed919f
编写于
11年前
作者:
B
Ben Skeggs
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
drm/nouveau/drm/pm: remove everything except the hwmon interfaces to THERM
Signed-off-by:
N
Ben Skeggs
<
bskeggs@redhat.com
>
上级
c52f4fa6
无相关合并请求
变更
14
展开全部
隐藏空白更改
内联
并排
Showing
14 changed file
with
69 addition
and
4722 deletion
+69
-4722
drivers/gpu/drm/nouveau/Makefile
drivers/gpu/drm/nouveau/Makefile
+1
-3
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_drm.c
+3
-6
drivers/gpu/drm/nouveau/nouveau_drm.h
drivers/gpu/drm/nouveau/nouveau_drm.h
+1
-1
drivers/gpu/drm/nouveau/nouveau_hwmon.c
drivers/gpu/drm/nouveau/nouveau_hwmon.c
+21
-539
drivers/gpu/drm/nouveau/nouveau_hwmon.h
drivers/gpu/drm/nouveau/nouveau_hwmon.h
+43
-0
drivers/gpu/drm/nouveau/nouveau_mem.c
drivers/gpu/drm/nouveau/nouveau_mem.c
+0
-647
drivers/gpu/drm/nouveau/nouveau_perf.c
drivers/gpu/drm/nouveau/nouveau_perf.c
+0
-416
drivers/gpu/drm/nouveau/nouveau_pm.h
drivers/gpu/drm/nouveau/nouveau_pm.h
+0
-283
drivers/gpu/drm/nouveau/nouveau_volt.c
drivers/gpu/drm/nouveau/nouveau_volt.c
+0
-250
drivers/gpu/drm/nouveau/nv04_pm.c
drivers/gpu/drm/nouveau/nv04_pm.c
+0
-146
drivers/gpu/drm/nouveau/nv40_pm.c
drivers/gpu/drm/nouveau/nv40_pm.c
+0
-353
drivers/gpu/drm/nouveau/nv50_pm.c
drivers/gpu/drm/nouveau/nv50_pm.c
+0
-855
drivers/gpu/drm/nouveau/nva3_pm.c
drivers/gpu/drm/nouveau/nva3_pm.c
+0
-624
drivers/gpu/drm/nouveau/nvc0_pm.c
drivers/gpu/drm/nouveau/nvc0_pm.c
+0
-599
未找到文件。
drivers/gpu/drm/nouveau/Makefile
浏览文件 @
b9ed919f
...
...
@@ -270,9 +270,7 @@ include $(src)/dispnv04/Makefile
nouveau-y
+=
nv50_display.o
# drm/pm
nouveau-y
+=
nouveau_pm.o nouveau_volt.o nouveau_perf.o
nouveau-y
+=
nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o
nouveau-y
+=
nouveau_mem.o
nouveau-y
+=
nouveau_hwmon.o
# other random bits
nouveau-$(CONFIG_COMPAT)
+=
nouveau_ioc32.o
...
...
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nouveau_drm.c
浏览文件 @
b9ed919f
...
...
@@ -46,7 +46,7 @@
#include "nouveau_gem.h"
#include "nouveau_agp.h"
#include "nouveau_vga.h"
#include "nouveau_
pm
.h"
#include "nouveau_
hwmon
.h"
#include "nouveau_acpi.h"
#include "nouveau_bios.h"
#include "nouveau_ioctl.h"
...
...
@@ -384,8 +384,7 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
goto
fail_dispinit
;
}
nouveau_pm_init
(
dev
);
nouveau_hwmon_init
(
dev
);
nouveau_accel_init
(
drm
);
nouveau_fbcon_init
(
dev
);
...
...
@@ -421,8 +420,7 @@ nouveau_drm_unload(struct drm_device *dev)
pm_runtime_get_sync
(
dev
->
dev
);
nouveau_fbcon_fini
(
dev
);
nouveau_accel_fini
(
drm
);
nouveau_pm_fini
(
dev
);
nouveau_hwmon_fini
(
dev
);
if
(
dev
->
mode_config
.
num_crtc
)
nouveau_display_fini
(
dev
);
...
...
@@ -562,7 +560,6 @@ nouveau_do_resume(struct drm_device *dev)
}
nouveau_run_vbios_init
(
dev
);
nouveau_pm_resume
(
dev
);
if
(
dev
->
mode_config
.
num_crtc
)
{
NV_INFO
(
drm
,
"resuming display...
\n
"
);
...
...
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nouveau_drm.h
浏览文件 @
b9ed919f
...
...
@@ -129,7 +129,7 @@ struct nouveau_drm {
struct
backlight_device
*
backlight
;
/* power management */
struct
nouveau_
pm
*
pm
;
struct
nouveau_
hwmon
*
hwmon
;
/* display power reference */
bool
have_disp_power_ref
;
...
...
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nouveau_
pm
.c
→
drivers/gpu/drm/nouveau/nouveau_
hwmon
.c
浏览文件 @
b9ed919f
...
...
@@ -32,369 +32,12 @@
#include <drm/drmP.h>
#include "nouveau_drm.h"
#include "nouveau_
pm
.h"
#include "nouveau_
hwmon
.h"
#include <subdev/gpio.h>
#include <subdev/timer.h>
#include <subdev/therm.h>
MODULE_PARM_DESC
(
perflvl
,
"Performance level (default: boot)"
);
static
char
*
nouveau_perflvl
;
module_param_named
(
perflvl
,
nouveau_perflvl
,
charp
,
0400
);
MODULE_PARM_DESC
(
perflvl_wr
,
"Allow perflvl changes (warning: dangerous!)"
);
static
int
nouveau_perflvl_wr
;
module_param_named
(
perflvl_wr
,
nouveau_perflvl_wr
,
int
,
0400
);
static
int
nouveau_pm_perflvl_aux
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
,
struct
nouveau_pm_level
*
a
,
struct
nouveau_pm_level
*
b
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_therm
*
therm
=
nouveau_therm
(
drm
->
device
);
int
ret
;
/*XXX: not on all boards, we should control based on temperature
* on recent boards.. or maybe on some other factor we don't
* know about?
*/
if
(
therm
&&
therm
->
fan_set
&&
a
->
fanspeed
&&
b
->
fanspeed
&&
b
->
fanspeed
>
a
->
fanspeed
)
{
ret
=
therm
->
fan_set
(
therm
,
perflvl
->
fanspeed
);
if
(
ret
&&
ret
!=
-
ENODEV
)
{
NV_ERROR
(
drm
,
"fanspeed set failed: %d
\n
"
,
ret
);
}
}
if
(
pm
->
voltage
.
supported
&&
pm
->
voltage_set
)
{
if
(
perflvl
->
volt_min
&&
b
->
volt_min
>
a
->
volt_min
)
{
ret
=
pm
->
voltage_set
(
dev
,
perflvl
->
volt_min
);
if
(
ret
)
{
NV_ERROR
(
drm
,
"voltage set failed: %d
\n
"
,
ret
);
return
ret
;
}
}
}
return
0
;
}
static
int
nouveau_pm_perflvl_set
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
void
*
state
;
int
ret
;
if
(
perflvl
==
pm
->
cur
)
return
0
;
ret
=
nouveau_pm_perflvl_aux
(
dev
,
perflvl
,
pm
->
cur
,
perflvl
);
if
(
ret
)
return
ret
;
state
=
pm
->
clocks_pre
(
dev
,
perflvl
);
if
(
IS_ERR
(
state
))
{
ret
=
PTR_ERR
(
state
);
goto
error
;
}
ret
=
pm
->
clocks_set
(
dev
,
state
);
if
(
ret
)
goto
error
;
ret
=
nouveau_pm_perflvl_aux
(
dev
,
perflvl
,
perflvl
,
pm
->
cur
);
if
(
ret
)
return
ret
;
pm
->
cur
=
perflvl
;
return
0
;
error:
/* restore the fan speed and voltage before leaving */
nouveau_pm_perflvl_aux
(
dev
,
perflvl
,
perflvl
,
pm
->
cur
);
return
ret
;
}
void
nouveau_pm_trigger
(
struct
drm_device
*
dev
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_timer
*
ptimer
=
nouveau_timer
(
drm
->
device
);
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_pm_profile
*
profile
=
NULL
;
struct
nouveau_pm_level
*
perflvl
=
NULL
;
int
ret
;
/* select power profile based on current power source */
if
(
power_supply_is_system_supplied
())
profile
=
pm
->
profile_ac
;
else
profile
=
pm
->
profile_dc
;
if
(
profile
!=
pm
->
profile
)
{
pm
->
profile
->
func
->
fini
(
pm
->
profile
);
pm
->
profile
=
profile
;
pm
->
profile
->
func
->
init
(
pm
->
profile
);
}
/* select performance level based on profile */
perflvl
=
profile
->
func
->
select
(
profile
);
/* change perflvl, if necessary */
if
(
perflvl
!=
pm
->
cur
)
{
u64
time0
=
ptimer
->
read
(
ptimer
);
NV_INFO
(
drm
,
"setting performance level: %d"
,
perflvl
->
id
);
ret
=
nouveau_pm_perflvl_set
(
dev
,
perflvl
);
if
(
ret
)
NV_INFO
(
drm
,
"> reclocking failed: %d
\n\n
"
,
ret
);
NV_INFO
(
drm
,
"> reclocking took %lluns
\n\n
"
,
ptimer
->
read
(
ptimer
)
-
time0
);
}
}
static
struct
nouveau_pm_profile
*
profile_find
(
struct
drm_device
*
dev
,
const
char
*
string
)
{
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_pm_profile
*
profile
;
list_for_each_entry
(
profile
,
&
pm
->
profiles
,
head
)
{
if
(
!
strncmp
(
profile
->
name
,
string
,
sizeof
(
profile
->
name
)))
return
profile
;
}
return
NULL
;
}
static
int
nouveau_pm_profile_set
(
struct
drm_device
*
dev
,
const
char
*
profile
)
{
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_pm_profile
*
ac
=
NULL
,
*
dc
=
NULL
;
char
string
[
16
],
*
cur
=
string
,
*
ptr
;
/* safety precaution, for now */
if
(
nouveau_perflvl_wr
!=
7777
)
return
-
EPERM
;
strncpy
(
string
,
profile
,
sizeof
(
string
));
string
[
sizeof
(
string
)
-
1
]
=
0
;
if
((
ptr
=
strchr
(
string
,
'\n'
)))
*
ptr
=
'\0'
;
ptr
=
strsep
(
&
cur
,
","
);
if
(
ptr
)
ac
=
profile_find
(
dev
,
ptr
);
ptr
=
strsep
(
&
cur
,
","
);
if
(
ptr
)
dc
=
profile_find
(
dev
,
ptr
);
else
dc
=
ac
;
if
(
ac
==
NULL
||
dc
==
NULL
)
return
-
EINVAL
;
pm
->
profile_ac
=
ac
;
pm
->
profile_dc
=
dc
;
nouveau_pm_trigger
(
dev
);
return
0
;
}
static
void
nouveau_pm_static_dummy
(
struct
nouveau_pm_profile
*
profile
)
{
}
static
struct
nouveau_pm_level
*
nouveau_pm_static_select
(
struct
nouveau_pm_profile
*
profile
)
{
return
container_of
(
profile
,
struct
nouveau_pm_level
,
profile
);
}
const
struct
nouveau_pm_profile_func
nouveau_pm_static_profile_func
=
{
.
destroy
=
nouveau_pm_static_dummy
,
.
init
=
nouveau_pm_static_dummy
,
.
fini
=
nouveau_pm_static_dummy
,
.
select
=
nouveau_pm_static_select
,
};
static
int
nouveau_pm_perflvl_get
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_therm
*
therm
=
nouveau_therm
(
drm
->
device
);
int
ret
;
memset
(
perflvl
,
0
,
sizeof
(
*
perflvl
));
if
(
pm
->
clocks_get
)
{
ret
=
pm
->
clocks_get
(
dev
,
perflvl
);
if
(
ret
)
return
ret
;
}
if
(
pm
->
voltage
.
supported
&&
pm
->
voltage_get
)
{
ret
=
pm
->
voltage_get
(
dev
);
if
(
ret
>
0
)
{
perflvl
->
volt_min
=
ret
;
perflvl
->
volt_max
=
ret
;
}
}
if
(
therm
&&
therm
->
fan_get
)
{
ret
=
therm
->
fan_get
(
therm
);
if
(
ret
>=
0
)
perflvl
->
fanspeed
=
ret
;
}
nouveau_mem_timing_read
(
dev
,
&
perflvl
->
timing
);
return
0
;
}
static
void
nouveau_pm_perflvl_info
(
struct
nouveau_pm_level
*
perflvl
,
char
*
ptr
,
int
len
)
{
char
c
[
16
],
s
[
16
],
v
[
32
],
f
[
16
],
m
[
16
];
c
[
0
]
=
'\0'
;
if
(
perflvl
->
core
)
snprintf
(
c
,
sizeof
(
c
),
" core %dMHz"
,
perflvl
->
core
/
1000
);
s
[
0
]
=
'\0'
;
if
(
perflvl
->
shader
)
snprintf
(
s
,
sizeof
(
s
),
" shader %dMHz"
,
perflvl
->
shader
/
1000
);
m
[
0
]
=
'\0'
;
if
(
perflvl
->
memory
)
snprintf
(
m
,
sizeof
(
m
),
" memory %dMHz"
,
perflvl
->
memory
/
1000
);
v
[
0
]
=
'\0'
;
if
(
perflvl
->
volt_min
&&
perflvl
->
volt_min
!=
perflvl
->
volt_max
)
{
snprintf
(
v
,
sizeof
(
v
),
" voltage %dmV-%dmV"
,
perflvl
->
volt_min
/
1000
,
perflvl
->
volt_max
/
1000
);
}
else
if
(
perflvl
->
volt_min
)
{
snprintf
(
v
,
sizeof
(
v
),
" voltage %dmV"
,
perflvl
->
volt_min
/
1000
);
}
f
[
0
]
=
'\0'
;
if
(
perflvl
->
fanspeed
)
snprintf
(
f
,
sizeof
(
f
),
" fanspeed %d%%"
,
perflvl
->
fanspeed
);
snprintf
(
ptr
,
len
,
"%s%s%s%s%s
\n
"
,
c
,
s
,
m
,
v
,
f
);
}
static
ssize_t
nouveau_pm_get_perflvl_info
(
struct
device
*
d
,
struct
device_attribute
*
a
,
char
*
buf
)
{
struct
nouveau_pm_level
*
perflvl
=
container_of
(
a
,
struct
nouveau_pm_level
,
dev_attr
);
char
*
ptr
=
buf
;
int
len
=
PAGE_SIZE
;
snprintf
(
ptr
,
len
,
"%d:"
,
perflvl
->
id
);
ptr
+=
strlen
(
buf
);
len
-=
strlen
(
buf
);
nouveau_pm_perflvl_info
(
perflvl
,
ptr
,
len
);
return
strlen
(
buf
);
}
static
ssize_t
nouveau_pm_get_perflvl
(
struct
device
*
d
,
struct
device_attribute
*
a
,
char
*
buf
)
{
struct
drm_device
*
dev
=
pci_get_drvdata
(
to_pci_dev
(
d
));
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_pm_level
cur
;
int
len
=
PAGE_SIZE
,
ret
;
char
*
ptr
=
buf
;
snprintf
(
ptr
,
len
,
"profile: %s, %s
\n
c:"
,
pm
->
profile_ac
->
name
,
pm
->
profile_dc
->
name
);
ptr
+=
strlen
(
buf
);
len
-=
strlen
(
buf
);
ret
=
nouveau_pm_perflvl_get
(
dev
,
&
cur
);
if
(
ret
==
0
)
nouveau_pm_perflvl_info
(
&
cur
,
ptr
,
len
);
return
strlen
(
buf
);
}
static
ssize_t
nouveau_pm_set_perflvl
(
struct
device
*
d
,
struct
device_attribute
*
a
,
const
char
*
buf
,
size_t
count
)
{
struct
drm_device
*
dev
=
pci_get_drvdata
(
to_pci_dev
(
d
));
int
ret
;
ret
=
nouveau_pm_profile_set
(
dev
,
buf
);
if
(
ret
)
return
ret
;
return
strlen
(
buf
);
}
static
DEVICE_ATTR
(
performance_level
,
S_IRUGO
|
S_IWUSR
,
nouveau_pm_get_perflvl
,
nouveau_pm_set_perflvl
);
static
int
nouveau_sysfs_init
(
struct
drm_device
*
dev
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
device
*
d
=
&
dev
->
pdev
->
dev
;
int
ret
,
i
;
ret
=
device_create_file
(
d
,
&
dev_attr_performance_level
);
if
(
ret
)
return
ret
;
for
(
i
=
0
;
i
<
pm
->
nr_perflvl
;
i
++
)
{
struct
nouveau_pm_level
*
perflvl
=
&
pm
->
perflvl
[
i
];
perflvl
->
dev_attr
.
attr
.
name
=
perflvl
->
name
;
perflvl
->
dev_attr
.
attr
.
mode
=
S_IRUGO
;
perflvl
->
dev_attr
.
show
=
nouveau_pm_get_perflvl_info
;
perflvl
->
dev_attr
.
store
=
NULL
;
sysfs_attr_init
(
&
perflvl
->
dev_attr
.
attr
);
ret
=
device_create_file
(
d
,
&
perflvl
->
dev_attr
);
if
(
ret
)
{
NV_ERROR
(
drm
,
"failed pervlvl %d sysfs: %d
\n
"
,
perflvl
->
id
,
i
);
perflvl
->
dev_attr
.
attr
.
name
=
NULL
;
nouveau_pm_fini
(
dev
);
return
ret
;
}
}
return
0
;
}
static
void
nouveau_sysfs_fini
(
struct
drm_device
*
dev
)
{
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
device
*
d
=
&
dev
->
pdev
->
dev
;
int
i
;
device_remove_file
(
d
,
&
dev_attr_performance_level
);
for
(
i
=
0
;
i
<
pm
->
nr_perflvl
;
i
++
)
{
struct
nouveau_pm_level
*
pl
=
&
pm
->
perflvl
[
i
];
if
(
!
pl
->
dev_attr
.
attr
.
name
)
break
;
device_remove_file
(
d
,
&
pl
->
dev_attr
);
}
}
#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
static
ssize_t
nouveau_hwmon_show_temp
(
struct
device
*
d
,
struct
device_attribute
*
a
,
char
*
buf
)
...
...
@@ -778,9 +421,6 @@ nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a,
int
ret
=
-
ENODEV
;
long
value
;
if
(
nouveau_perflvl_wr
!=
7777
)
return
-
EPERM
;
if
(
kstrtol
(
buf
,
10
,
&
value
)
==
-
EINVAL
)
return
-
EINVAL
;
...
...
@@ -919,17 +559,21 @@ static const struct attribute_group hwmon_pwm_fan_attrgroup = {
};
#endif
static
int
int
nouveau_hwmon_init
(
struct
drm_device
*
dev
)
{
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_therm
*
therm
=
nouveau_therm
(
drm
->
device
);
struct
nouveau_hwmon
*
hwmon
;
struct
device
*
hwmon_dev
;
int
ret
=
0
;
hwmon
=
drm
->
hwmon
=
kzalloc
(
sizeof
(
*
hwmon
),
GFP_KERNEL
);
if
(
!
hwmon
)
return
-
ENOMEM
;
hwmon
->
dev
=
dev
;
if
(
!
therm
||
!
therm
->
temp_get
||
!
therm
->
attr_get
||
!
therm
->
attr_set
)
return
-
ENODEV
;
...
...
@@ -976,199 +620,37 @@ nouveau_hwmon_init(struct drm_device *dev)
goto
error
;
}
pm
->
hwmon
=
hwmon_dev
;
hwmon
->
hwmon
=
hwmon_dev
;
return
0
;
error:
NV_ERROR
(
drm
,
"Unable to create some hwmon sysfs files: %d
\n
"
,
ret
);
hwmon_device_unregister
(
hwmon_dev
);
pm
->
hwmon
=
NULL
;
hwmon
->
hwmon
=
NULL
;
return
ret
;
#else
pm
->
hwmon
=
NULL
;
hwmon
->
hwmon
=
NULL
;
return
0
;
#endif
}
static
void
void
nouveau_hwmon_fini
(
struct
drm_device
*
dev
)
{
#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
struct
nouveau_
pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_
hwmon
*
hwmon
=
nouveau_hwmon
(
dev
);
if
(
pm
->
hwmon
)
{
sysfs_remove_group
(
&
pm
->
hwmon
->
kobj
,
&
hwmon_default_attrgroup
);
sysfs_remove_group
(
&
pm
->
hwmon
->
kobj
,
&
hwmon_temp_attrgroup
);
sysfs_remove_group
(
&
pm
->
hwmon
->
kobj
,
&
hwmon_pwm_fan_attrgroup
);
sysfs_remove_group
(
&
pm
->
hwmon
->
kobj
,
&
hwmon_fan_rpm_attrgroup
);
if
(
hwmon
->
hwmon
)
{
sysfs_remove_group
(
&
hwmon
->
hwmon
->
kobj
,
&
hwmon_default_attrgroup
);
sysfs_remove_group
(
&
hwmon
->
hwmon
->
kobj
,
&
hwmon_temp_attrgroup
);
sysfs_remove_group
(
&
hwmon
->
hwmon
->
kobj
,
&
hwmon_pwm_fan_attrgroup
);
sysfs_remove_group
(
&
hwmon
->
hwmon
->
kobj
,
&
hwmon_fan_rpm_attrgroup
);
hwmon_device_unregister
(
pm
->
hwmon
);
hwmon_device_unregister
(
hwmon
->
hwmon
);
}
#endif
}
#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
static
int
nouveau_pm_acpi_event
(
struct
notifier_block
*
nb
,
unsigned
long
val
,
void
*
data
)
{
struct
nouveau_pm
*
pm
=
container_of
(
nb
,
struct
nouveau_pm
,
acpi_nb
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
pm
->
dev
);
struct
acpi_bus_event
*
entry
=
(
struct
acpi_bus_event
*
)
data
;
if
(
strcmp
(
entry
->
device_class
,
"ac_adapter"
)
==
0
)
{
bool
ac
=
power_supply_is_system_supplied
();
NV_DEBUG
(
drm
,
"power supply changed: %s
\n
"
,
ac
?
"AC"
:
"DC"
);
nouveau_pm_trigger
(
pm
->
dev
);
}
return
NOTIFY_OK
;
}
nouveau_drm
(
dev
)
->
hwmon
=
NULL
;
kfree
(
hwmon
);
#endif
int
nouveau_pm_init
(
struct
drm_device
*
dev
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_pm
*
pm
;
char
info
[
256
];
int
ret
,
i
;
pm
=
drm
->
pm
=
kzalloc
(
sizeof
(
*
pm
),
GFP_KERNEL
);
if
(
!
pm
)
return
-
ENOMEM
;
pm
->
dev
=
dev
;
if
(
device
->
card_type
<
NV_40
)
{
pm
->
clocks_get
=
nv04_pm_clocks_get
;
pm
->
clocks_pre
=
nv04_pm_clocks_pre
;
pm
->
clocks_set
=
nv04_pm_clocks_set
;
if
(
nouveau_gpio
(
drm
->
device
))
{
pm
->
voltage_get
=
nouveau_voltage_gpio_get
;
pm
->
voltage_set
=
nouveau_voltage_gpio_set
;
}
}
else
if
(
device
->
card_type
<
NV_50
)
{
pm
->
clocks_get
=
nv40_pm_clocks_get
;
pm
->
clocks_pre
=
nv40_pm_clocks_pre
;
pm
->
clocks_set
=
nv40_pm_clocks_set
;
pm
->
voltage_get
=
nouveau_voltage_gpio_get
;
pm
->
voltage_set
=
nouveau_voltage_gpio_set
;
}
else
if
(
device
->
card_type
<
NV_C0
)
{
if
(
device
->
chipset
<
0xa3
||
device
->
chipset
==
0xaa
||
device
->
chipset
==
0xac
)
{
pm
->
clocks_get
=
nv50_pm_clocks_get
;
pm
->
clocks_pre
=
nv50_pm_clocks_pre
;
pm
->
clocks_set
=
nv50_pm_clocks_set
;
}
else
{
pm
->
clocks_get
=
nva3_pm_clocks_get
;
pm
->
clocks_pre
=
nva3_pm_clocks_pre
;
pm
->
clocks_set
=
nva3_pm_clocks_set
;
}
pm
->
voltage_get
=
nouveau_voltage_gpio_get
;
pm
->
voltage_set
=
nouveau_voltage_gpio_set
;
}
else
if
(
device
->
card_type
<
NV_E0
)
{
pm
->
clocks_get
=
nvc0_pm_clocks_get
;
pm
->
clocks_pre
=
nvc0_pm_clocks_pre
;
pm
->
clocks_set
=
nvc0_pm_clocks_set
;
pm
->
voltage_get
=
nouveau_voltage_gpio_get
;
pm
->
voltage_set
=
nouveau_voltage_gpio_set
;
}
/* parse aux tables from vbios */
nouveau_volt_init
(
dev
);
INIT_LIST_HEAD
(
&
pm
->
profiles
);
/* determine current ("boot") performance level */
ret
=
nouveau_pm_perflvl_get
(
dev
,
&
pm
->
boot
);
if
(
ret
)
{
NV_ERROR
(
drm
,
"failed to determine boot perflvl
\n
"
);
return
ret
;
}
strncpy
(
pm
->
boot
.
name
,
"boot"
,
4
);
strncpy
(
pm
->
boot
.
profile
.
name
,
"boot"
,
4
);
pm
->
boot
.
profile
.
func
=
&
nouveau_pm_static_profile_func
;
list_add
(
&
pm
->
boot
.
profile
.
head
,
&
pm
->
profiles
);
pm
->
profile_ac
=
&
pm
->
boot
.
profile
;
pm
->
profile_dc
=
&
pm
->
boot
.
profile
;
pm
->
profile
=
&
pm
->
boot
.
profile
;
pm
->
cur
=
&
pm
->
boot
;
/* add performance levels from vbios */
nouveau_perf_init
(
dev
);
/* display available performance levels */
NV_INFO
(
drm
,
"%d available performance level(s)
\n
"
,
pm
->
nr_perflvl
);
for
(
i
=
0
;
i
<
pm
->
nr_perflvl
;
i
++
)
{
nouveau_pm_perflvl_info
(
&
pm
->
perflvl
[
i
],
info
,
sizeof
(
info
));
NV_INFO
(
drm
,
"%d:%s"
,
pm
->
perflvl
[
i
].
id
,
info
);
}
nouveau_pm_perflvl_info
(
&
pm
->
boot
,
info
,
sizeof
(
info
));
NV_INFO
(
drm
,
"c:%s"
,
info
);
/* switch performance levels now if requested */
if
(
nouveau_perflvl
!=
NULL
)
nouveau_pm_profile_set
(
dev
,
nouveau_perflvl
);
nouveau_sysfs_init
(
dev
);
nouveau_hwmon_init
(
dev
);
#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
pm
->
acpi_nb
.
notifier_call
=
nouveau_pm_acpi_event
;
register_acpi_notifier
(
&
pm
->
acpi_nb
);
#endif
return
0
;
}
void
nouveau_pm_fini
(
struct
drm_device
*
dev
)
{
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_pm_profile
*
profile
,
*
tmp
;
list_for_each_entry_safe
(
profile
,
tmp
,
&
pm
->
profiles
,
head
)
{
list_del
(
&
profile
->
head
);
profile
->
func
->
destroy
(
profile
);
}
if
(
pm
->
cur
!=
&
pm
->
boot
)
nouveau_pm_perflvl_set
(
dev
,
&
pm
->
boot
);
nouveau_perf_fini
(
dev
);
nouveau_volt_fini
(
dev
);
#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
unregister_acpi_notifier
(
&
pm
->
acpi_nb
);
#endif
nouveau_hwmon_fini
(
dev
);
nouveau_sysfs_fini
(
dev
);
nouveau_drm
(
dev
)
->
pm
=
NULL
;
kfree
(
pm
);
}
void
nouveau_pm_resume
(
struct
drm_device
*
dev
)
{
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_pm_level
*
perflvl
;
if
(
!
pm
->
cur
||
pm
->
cur
==
&
pm
->
boot
)
return
;
perflvl
=
pm
->
cur
;
pm
->
cur
=
&
pm
->
boot
;
nouveau_pm_perflvl_set
(
dev
,
perflvl
);
}
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nouveau_hw
sq
.h
→
drivers/gpu/drm/nouveau/nouveau_hw
mon
.h
浏览文件 @
b9ed919f
...
...
@@ -22,94 +22,22 @@
* Authors: Ben Skeggs
*/
#ifndef __NOUVEAU_
HWSQ
_H__
#define __NOUVEAU_
HWSQ
_H__
#ifndef __NOUVEAU_
PM
_H__
#define __NOUVEAU_
PM
_H__
struct
hwsq_ucode
{
u8
data
[
0x200
];
union
{
u8
*
u08
;
u16
*
u16
;
u32
*
u32
;
}
ptr
;
u16
len
;
u32
reg
;
u32
val
;
struct
nouveau_hwmon
{
struct
drm_device
*
dev
;
struct
device
*
hwmon
;
};
static
inline
void
hwsq_init
(
struct
hwsq_ucode
*
hwsq
)
{
hwsq
->
ptr
.
u08
=
hwsq
->
data
;
hwsq
->
reg
=
0xffffffff
;
hwsq
->
val
=
0xffffffff
;
}
static
inline
void
hwsq_fini
(
struct
hwsq_ucode
*
hwsq
)
{
do
{
*
hwsq
->
ptr
.
u08
++
=
0x7f
;
hwsq
->
len
=
hwsq
->
ptr
.
u08
-
hwsq
->
data
;
}
while
(
hwsq
->
len
&
3
);
hwsq
->
ptr
.
u08
=
hwsq
->
data
;
}
static
inline
void
hwsq_usec
(
struct
hwsq_ucode
*
hwsq
,
u8
usec
)
{
u32
shift
=
0
;
while
(
usec
&
~
3
)
{
usec
>>=
2
;
shift
++
;
}
*
hwsq
->
ptr
.
u08
++
=
(
shift
<<
2
)
|
usec
;
}
static
inline
void
hwsq_setf
(
struct
hwsq_ucode
*
hwsq
,
u8
flag
,
int
val
)
{
flag
+=
0x80
;
if
(
val
>=
0
)
flag
+=
0x20
;
if
(
val
>=
1
)
flag
+=
0x20
;
*
hwsq
->
ptr
.
u08
++
=
flag
;
}
static
inline
void
hwsq_op5f
(
struct
hwsq_ucode
*
hwsq
,
u8
v0
,
u8
v1
)
static
inline
struct
nouveau_hwmon
*
nouveau_hwmon
(
struct
drm_device
*
dev
)
{
*
hwsq
->
ptr
.
u08
++
=
0x5f
;
*
hwsq
->
ptr
.
u08
++
=
v0
;
*
hwsq
->
ptr
.
u08
++
=
v1
;
return
nouveau_drm
(
dev
)
->
hwmon
;
}
static
inline
void
hwsq_wr32
(
struct
hwsq_ucode
*
hwsq
,
u32
reg
,
u32
val
)
{
if
(
val
!=
hwsq
->
val
)
{
if
((
val
&
0xffff0000
)
==
(
hwsq
->
val
&
0xffff0000
))
{
*
hwsq
->
ptr
.
u08
++
=
0x42
;
*
hwsq
->
ptr
.
u16
++
=
(
val
&
0x0000ffff
);
}
else
{
*
hwsq
->
ptr
.
u08
++
=
0xe2
;
*
hwsq
->
ptr
.
u32
++
=
val
;
}
hwsq
->
val
=
val
;
}
if
((
reg
&
0xffff0000
)
==
(
hwsq
->
reg
&
0xffff0000
))
{
*
hwsq
->
ptr
.
u08
++
=
0x40
;
*
hwsq
->
ptr
.
u16
++
=
(
reg
&
0x0000ffff
);
}
else
{
*
hwsq
->
ptr
.
u08
++
=
0xe0
;
*
hwsq
->
ptr
.
u32
++
=
reg
;
}
hwsq
->
reg
=
reg
;
}
/* nouveau_hwmon.c */
int
nouveau_hwmon_init
(
struct
drm_device
*
dev
);
void
nouveau_hwmon_fini
(
struct
drm_device
*
dev
);
#endif
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nouveau_mem.c
已删除
100644 → 0
浏览文件 @
c52f4fa6
/*
* Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.
* Copyright 2005 Stephane Marchesin
*
* The Weather Channel (TM) funded Tungsten Graphics to develop the
* initial release of the Radeon 8500 driver under the XFree86 license.
* This notice must be preserved.
*
* 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 AUTHORS AND/OR THEIR 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.
*
* Authors:
* Ben Skeggs <bskeggs@redhat.com>
* Roy Spliet <r.spliet@student.tudelft.nl>
*/
#include "nouveau_drm.h"
#include "nouveau_pm.h"
#include <subdev/fb.h>
static
int
nv40_mem_timing_calc
(
struct
drm_device
*
dev
,
u32
freq
,
struct
nouveau_pm_tbl_entry
*
e
,
u8
len
,
struct
nouveau_pm_memtiming
*
boot
,
struct
nouveau_pm_memtiming
*
t
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
t
->
reg
[
0
]
=
(
e
->
tRP
<<
24
|
e
->
tRAS
<<
16
|
e
->
tRFC
<<
8
|
e
->
tRC
);
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere! */
t
->
reg
[
1
]
=
(
e
->
tWR
+
2
+
(
t
->
tCWL
-
1
))
<<
24
|
1
<<
16
|
(
e
->
tWTR
+
2
+
(
t
->
tCWL
-
1
))
<<
8
|
(
e
->
tCL
+
2
-
(
t
->
tCWL
-
1
));
t
->
reg
[
2
]
=
0x20200000
|
((
t
->
tCWL
-
1
)
<<
24
|
e
->
tRRD
<<
16
|
e
->
tRCDWR
<<
8
|
e
->
tRCDRD
);
NV_DEBUG
(
drm
,
"Entry %d: 220: %08x %08x %08x
\n
"
,
t
->
id
,
t
->
reg
[
0
],
t
->
reg
[
1
],
t
->
reg
[
2
]);
return
0
;
}
static
int
nv50_mem_timing_calc
(
struct
drm_device
*
dev
,
u32
freq
,
struct
nouveau_pm_tbl_entry
*
e
,
u8
len
,
struct
nouveau_pm_memtiming
*
boot
,
struct
nouveau_pm_memtiming
*
t
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_fb
*
pfb
=
nouveau_fb
(
device
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
bit_entry
P
;
uint8_t
unk18
=
1
,
unk20
=
0
,
unk21
=
0
,
tmp7_3
;
if
(
bit_table
(
dev
,
'P'
,
&
P
))
return
-
EINVAL
;
switch
(
min
(
len
,
(
u8
)
22
))
{
case
22
:
unk21
=
e
->
tUNK_21
;
case
21
:
unk20
=
e
->
tUNK_20
;
case
20
:
if
(
e
->
tCWL
>
0
)
t
->
tCWL
=
e
->
tCWL
;
case
19
:
unk18
=
e
->
tUNK_18
;
break
;
}
t
->
reg
[
0
]
=
(
e
->
tRP
<<
24
|
e
->
tRAS
<<
16
|
e
->
tRFC
<<
8
|
e
->
tRC
);
t
->
reg
[
1
]
=
(
e
->
tWR
+
2
+
(
t
->
tCWL
-
1
))
<<
24
|
max
(
unk18
,
(
u8
)
1
)
<<
16
|
(
e
->
tWTR
+
2
+
(
t
->
tCWL
-
1
))
<<
8
;
t
->
reg
[
2
]
=
((
t
->
tCWL
-
1
)
<<
24
|
e
->
tRRD
<<
16
|
e
->
tRCDWR
<<
8
|
e
->
tRCDRD
);
t
->
reg
[
4
]
=
e
->
tUNK_13
<<
8
|
e
->
tUNK_13
;
t
->
reg
[
5
]
=
(
e
->
tRFC
<<
24
|
max
(
e
->
tRCDRD
,
e
->
tRCDWR
)
<<
16
|
e
->
tRP
);
t
->
reg
[
8
]
=
boot
->
reg
[
8
]
&
0xffffff00
;
if
(
P
.
version
==
1
)
{
t
->
reg
[
1
]
|=
(
e
->
tCL
+
2
-
(
t
->
tCWL
-
1
));
t
->
reg
[
3
]
=
(
0x14
+
e
->
tCL
)
<<
24
|
0x16
<<
16
|
(
e
->
tCL
-
1
)
<<
8
|
(
e
->
tCL
-
1
);
t
->
reg
[
4
]
|=
boot
->
reg
[
4
]
&
0xffff0000
;
t
->
reg
[
6
]
=
(
0x33
-
t
->
tCWL
)
<<
16
|
t
->
tCWL
<<
8
|
(
0x2e
+
e
->
tCL
-
t
->
tCWL
);
t
->
reg
[
7
]
=
0x4000202
|
(
e
->
tCL
-
1
)
<<
16
;
/* XXX: P.version == 1 only has DDR2 and GDDR3? */
if
(
pfb
->
ram
->
type
==
NV_MEM_TYPE_DDR2
)
{
t
->
reg
[
5
]
|=
(
e
->
tCL
+
3
)
<<
8
;
t
->
reg
[
6
]
|=
(
t
->
tCWL
-
2
)
<<
8
;
t
->
reg
[
8
]
|=
(
e
->
tCL
-
4
);
}
else
{
t
->
reg
[
5
]
|=
(
e
->
tCL
+
2
)
<<
8
;
t
->
reg
[
6
]
|=
t
->
tCWL
<<
8
;
t
->
reg
[
8
]
|=
(
e
->
tCL
-
2
);
}
}
else
{
t
->
reg
[
1
]
|=
(
5
+
e
->
tCL
-
(
t
->
tCWL
));
/* XXX: 0xb? 0x30? */
t
->
reg
[
3
]
=
(
0x30
+
e
->
tCL
)
<<
24
|
(
boot
->
reg
[
3
]
&
0x00ff0000
)
|
(
0xb
+
e
->
tCL
)
<<
8
|
(
e
->
tCL
-
1
);
t
->
reg
[
4
]
|=
(
unk20
<<
24
|
unk21
<<
16
);
/* XXX: +6? */
t
->
reg
[
5
]
|=
(
t
->
tCWL
+
6
)
<<
8
;
t
->
reg
[
6
]
=
(
0x5a
+
e
->
tCL
)
<<
16
|
(
6
-
e
->
tCL
+
t
->
tCWL
)
<<
8
|
(
0x50
+
e
->
tCL
-
t
->
tCWL
);
tmp7_3
=
(
boot
->
reg
[
7
]
&
0xff000000
)
>>
24
;
t
->
reg
[
7
]
=
(
tmp7_3
<<
24
)
|
((
tmp7_3
-
6
+
e
->
tCL
)
<<
16
)
|
0x202
;
}
NV_DEBUG
(
drm
,
"Entry %d: 220: %08x %08x %08x %08x
\n
"
,
t
->
id
,
t
->
reg
[
0
],
t
->
reg
[
1
],
t
->
reg
[
2
],
t
->
reg
[
3
]);
NV_DEBUG
(
drm
,
" 230: %08x %08x %08x %08x
\n
"
,
t
->
reg
[
4
],
t
->
reg
[
5
],
t
->
reg
[
6
],
t
->
reg
[
7
]);
NV_DEBUG
(
drm
,
" 240: %08x
\n
"
,
t
->
reg
[
8
]);
return
0
;
}
static
int
nvc0_mem_timing_calc
(
struct
drm_device
*
dev
,
u32
freq
,
struct
nouveau_pm_tbl_entry
*
e
,
u8
len
,
struct
nouveau_pm_memtiming
*
boot
,
struct
nouveau_pm_memtiming
*
t
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
if
(
e
->
tCWL
>
0
)
t
->
tCWL
=
e
->
tCWL
;
t
->
reg
[
0
]
=
(
e
->
tRP
<<
24
|
(
e
->
tRAS
&
0x7f
)
<<
17
|
e
->
tRFC
<<
8
|
e
->
tRC
);
t
->
reg
[
1
]
=
(
boot
->
reg
[
1
]
&
0xff000000
)
|
(
e
->
tRCDWR
&
0x0f
)
<<
20
|
(
e
->
tRCDRD
&
0x0f
)
<<
14
|
(
t
->
tCWL
<<
7
)
|
(
e
->
tCL
&
0x0f
);
t
->
reg
[
2
]
=
(
boot
->
reg
[
2
]
&
0xff0000ff
)
|
e
->
tWR
<<
16
|
e
->
tWTR
<<
8
;
t
->
reg
[
3
]
=
(
e
->
tUNK_20
&
0x1f
)
<<
9
|
(
e
->
tUNK_21
&
0xf
)
<<
5
|
(
e
->
tUNK_13
&
0x1f
);
t
->
reg
[
4
]
=
(
boot
->
reg
[
4
]
&
0xfff00fff
)
|
(
e
->
tRRD
&
0x1f
)
<<
15
;
NV_DEBUG
(
drm
,
"Entry %d: 290: %08x %08x %08x %08x
\n
"
,
t
->
id
,
t
->
reg
[
0
],
t
->
reg
[
1
],
t
->
reg
[
2
],
t
->
reg
[
3
]);
NV_DEBUG
(
drm
,
" 2a0: %08x
\n
"
,
t
->
reg
[
4
]);
return
0
;
}
/**
* MR generation methods
*/
static
int
nouveau_mem_ddr2_mr
(
struct
drm_device
*
dev
,
u32
freq
,
struct
nouveau_pm_tbl_entry
*
e
,
u8
len
,
struct
nouveau_pm_memtiming
*
boot
,
struct
nouveau_pm_memtiming
*
t
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
t
->
drive_strength
=
0
;
if
(
len
<
15
)
{
t
->
odt
=
boot
->
odt
;
}
else
{
t
->
odt
=
e
->
RAM_FT1
&
0x07
;
}
if
(
e
->
tCL
>=
NV_MEM_CL_DDR2_MAX
)
{
NV_WARN
(
drm
,
"(%u) Invalid tCL: %u"
,
t
->
id
,
e
->
tCL
);
return
-
ERANGE
;
}
if
(
e
->
tWR
>=
NV_MEM_WR_DDR2_MAX
)
{
NV_WARN
(
drm
,
"(%u) Invalid tWR: %u"
,
t
->
id
,
e
->
tWR
);
return
-
ERANGE
;
}
if
(
t
->
odt
>
3
)
{
NV_WARN
(
drm
,
"(%u) Invalid odt value, assuming disabled: %x"
,
t
->
id
,
t
->
odt
);
t
->
odt
=
0
;
}
t
->
mr
[
0
]
=
(
boot
->
mr
[
0
]
&
0x100f
)
|
(
e
->
tCL
)
<<
4
|
(
e
->
tWR
-
1
)
<<
9
;
t
->
mr
[
1
]
=
(
boot
->
mr
[
1
]
&
0x101fbb
)
|
(
t
->
odt
&
0x1
)
<<
2
|
(
t
->
odt
&
0x2
)
<<
5
;
NV_DEBUG
(
drm
,
"(%u) MR: %08x"
,
t
->
id
,
t
->
mr
[
0
]);
return
0
;
}
static
const
uint8_t
nv_mem_wr_lut_ddr3
[
NV_MEM_WR_DDR3_MAX
]
=
{
0
,
0
,
0
,
0
,
0
,
1
,
2
,
3
,
4
,
5
,
5
,
6
,
6
,
7
,
7
,
0
,
0
};
static
int
nouveau_mem_ddr3_mr
(
struct
drm_device
*
dev
,
u32
freq
,
struct
nouveau_pm_tbl_entry
*
e
,
u8
len
,
struct
nouveau_pm_memtiming
*
boot
,
struct
nouveau_pm_memtiming
*
t
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
u8
cl
=
e
->
tCL
-
4
;
t
->
drive_strength
=
0
;
if
(
len
<
15
)
{
t
->
odt
=
boot
->
odt
;
}
else
{
t
->
odt
=
e
->
RAM_FT1
&
0x07
;
}
if
(
e
->
tCL
>=
NV_MEM_CL_DDR3_MAX
||
e
->
tCL
<
4
)
{
NV_WARN
(
drm
,
"(%u) Invalid tCL: %u"
,
t
->
id
,
e
->
tCL
);
return
-
ERANGE
;
}
if
(
e
->
tWR
>=
NV_MEM_WR_DDR3_MAX
||
e
->
tWR
<
4
)
{
NV_WARN
(
drm
,
"(%u) Invalid tWR: %u"
,
t
->
id
,
e
->
tWR
);
return
-
ERANGE
;
}
if
(
e
->
tCWL
<
5
)
{
NV_WARN
(
drm
,
"(%u) Invalid tCWL: %u"
,
t
->
id
,
e
->
tCWL
);
return
-
ERANGE
;
}
t
->
mr
[
0
]
=
(
boot
->
mr
[
0
]
&
0x180b
)
|
/* CAS */
(
cl
&
0x7
)
<<
4
|
(
cl
&
0x8
)
>>
1
|
(
nv_mem_wr_lut_ddr3
[
e
->
tWR
])
<<
9
;
t
->
mr
[
1
]
=
(
boot
->
mr
[
1
]
&
0x101dbb
)
|
(
t
->
odt
&
0x1
)
<<
2
|
(
t
->
odt
&
0x2
)
<<
5
|
(
t
->
odt
&
0x4
)
<<
7
;
t
->
mr
[
2
]
=
(
boot
->
mr
[
2
]
&
0x20ffb7
)
|
(
e
->
tCWL
-
5
)
<<
3
;
NV_DEBUG
(
drm
,
"(%u) MR: %08x %08x"
,
t
->
id
,
t
->
mr
[
0
],
t
->
mr
[
2
]);
return
0
;
}
static
const
uint8_t
nv_mem_cl_lut_gddr3
[
NV_MEM_CL_GDDR3_MAX
]
=
{
0
,
0
,
0
,
0
,
4
,
5
,
6
,
7
,
0
,
1
,
2
,
3
,
8
,
9
,
10
,
11
};
static
const
uint8_t
nv_mem_wr_lut_gddr3
[
NV_MEM_WR_GDDR3_MAX
]
=
{
0
,
0
,
0
,
0
,
0
,
2
,
3
,
8
,
9
,
10
,
11
,
0
,
0
,
1
,
1
,
0
,
3
};
static
int
nouveau_mem_gddr3_mr
(
struct
drm_device
*
dev
,
u32
freq
,
struct
nouveau_pm_tbl_entry
*
e
,
u8
len
,
struct
nouveau_pm_memtiming
*
boot
,
struct
nouveau_pm_memtiming
*
t
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
if
(
len
<
15
)
{
t
->
drive_strength
=
boot
->
drive_strength
;
t
->
odt
=
boot
->
odt
;
}
else
{
t
->
drive_strength
=
(
e
->
RAM_FT1
&
0x30
)
>>
4
;
t
->
odt
=
e
->
RAM_FT1
&
0x07
;
}
if
(
e
->
tCL
>=
NV_MEM_CL_GDDR3_MAX
)
{
NV_WARN
(
drm
,
"(%u) Invalid tCL: %u"
,
t
->
id
,
e
->
tCL
);
return
-
ERANGE
;
}
if
(
e
->
tWR
>=
NV_MEM_WR_GDDR3_MAX
)
{
NV_WARN
(
drm
,
"(%u) Invalid tWR: %u"
,
t
->
id
,
e
->
tWR
);
return
-
ERANGE
;
}
if
(
t
->
odt
>
3
)
{
NV_WARN
(
drm
,
"(%u) Invalid odt value, assuming autocal: %x"
,
t
->
id
,
t
->
odt
);
t
->
odt
=
0
;
}
t
->
mr
[
0
]
=
(
boot
->
mr
[
0
]
&
0xe0b
)
|
/* CAS */
((
nv_mem_cl_lut_gddr3
[
e
->
tCL
]
&
0x7
)
<<
4
)
|
((
nv_mem_cl_lut_gddr3
[
e
->
tCL
]
&
0x8
)
>>
2
);
t
->
mr
[
1
]
=
(
boot
->
mr
[
1
]
&
0x100f40
)
|
t
->
drive_strength
|
(
t
->
odt
<<
2
)
|
(
nv_mem_wr_lut_gddr3
[
e
->
tWR
]
&
0xf
)
<<
4
;
t
->
mr
[
2
]
=
boot
->
mr
[
2
];
NV_DEBUG
(
drm
,
"(%u) MR: %08x %08x %08x"
,
t
->
id
,
t
->
mr
[
0
],
t
->
mr
[
1
],
t
->
mr
[
2
]);
return
0
;
}
static
int
nouveau_mem_gddr5_mr
(
struct
drm_device
*
dev
,
u32
freq
,
struct
nouveau_pm_tbl_entry
*
e
,
u8
len
,
struct
nouveau_pm_memtiming
*
boot
,
struct
nouveau_pm_memtiming
*
t
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
if
(
len
<
15
)
{
t
->
drive_strength
=
boot
->
drive_strength
;
t
->
odt
=
boot
->
odt
;
}
else
{
t
->
drive_strength
=
(
e
->
RAM_FT1
&
0x30
)
>>
4
;
t
->
odt
=
e
->
RAM_FT1
&
0x03
;
}
if
(
e
->
tCL
>=
NV_MEM_CL_GDDR5_MAX
)
{
NV_WARN
(
drm
,
"(%u) Invalid tCL: %u"
,
t
->
id
,
e
->
tCL
);
return
-
ERANGE
;
}
if
(
e
->
tWR
>=
NV_MEM_WR_GDDR5_MAX
)
{
NV_WARN
(
drm
,
"(%u) Invalid tWR: %u"
,
t
->
id
,
e
->
tWR
);
return
-
ERANGE
;
}
if
(
t
->
odt
>
3
)
{
NV_WARN
(
drm
,
"(%u) Invalid odt value, assuming autocal: %x"
,
t
->
id
,
t
->
odt
);
t
->
odt
=
0
;
}
t
->
mr
[
0
]
=
(
boot
->
mr
[
0
]
&
0x007
)
|
((
e
->
tCL
-
5
)
<<
3
)
|
((
e
->
tWR
-
4
)
<<
8
);
t
->
mr
[
1
]
=
(
boot
->
mr
[
1
]
&
0x1007f0
)
|
t
->
drive_strength
|
(
t
->
odt
<<
2
);
NV_DEBUG
(
drm
,
"(%u) MR: %08x %08x"
,
t
->
id
,
t
->
mr
[
0
],
t
->
mr
[
1
]);
return
0
;
}
int
nouveau_mem_timing_calc
(
struct
drm_device
*
dev
,
u32
freq
,
struct
nouveau_pm_memtiming
*
t
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_fb
*
pfb
=
nouveau_fb
(
device
);
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_pm_memtiming
*
boot
=
&
pm
->
boot
.
timing
;
struct
nouveau_pm_tbl_entry
*
e
;
u8
ver
,
len
,
*
ptr
,
*
ramcfg
;
int
ret
;
ptr
=
nouveau_perf_timing
(
dev
,
freq
,
&
ver
,
&
len
);
if
(
!
ptr
||
ptr
[
0
]
==
0x00
)
{
*
t
=
*
boot
;
return
0
;
}
e
=
(
struct
nouveau_pm_tbl_entry
*
)
ptr
;
t
->
tCWL
=
boot
->
tCWL
;
switch
(
device
->
card_type
)
{
case
NV_40
:
ret
=
nv40_mem_timing_calc
(
dev
,
freq
,
e
,
len
,
boot
,
t
);
break
;
case
NV_50
:
ret
=
nv50_mem_timing_calc
(
dev
,
freq
,
e
,
len
,
boot
,
t
);
break
;
case
NV_C0
:
case
NV_D0
:
ret
=
nvc0_mem_timing_calc
(
dev
,
freq
,
e
,
len
,
boot
,
t
);
break
;
default:
ret
=
-
ENODEV
;
break
;
}
switch
(
pfb
->
ram
->
type
*
!
ret
)
{
case
NV_MEM_TYPE_GDDR3
:
ret
=
nouveau_mem_gddr3_mr
(
dev
,
freq
,
e
,
len
,
boot
,
t
);
break
;
case
NV_MEM_TYPE_GDDR5
:
ret
=
nouveau_mem_gddr5_mr
(
dev
,
freq
,
e
,
len
,
boot
,
t
);
break
;
case
NV_MEM_TYPE_DDR2
:
ret
=
nouveau_mem_ddr2_mr
(
dev
,
freq
,
e
,
len
,
boot
,
t
);
break
;
case
NV_MEM_TYPE_DDR3
:
ret
=
nouveau_mem_ddr3_mr
(
dev
,
freq
,
e
,
len
,
boot
,
t
);
break
;
default:
ret
=
-
EINVAL
;
break
;
}
ramcfg
=
nouveau_perf_ramcfg
(
dev
,
freq
,
&
ver
,
&
len
);
if
(
ramcfg
)
{
int
dll_off
;
if
(
ver
==
0x00
)
dll_off
=
!!
(
ramcfg
[
3
]
&
0x04
);
else
dll_off
=
!!
(
ramcfg
[
2
]
&
0x40
);
switch
(
pfb
->
ram
->
type
)
{
case
NV_MEM_TYPE_GDDR3
:
t
->
mr
[
1
]
&=
~
0x00000040
;
t
->
mr
[
1
]
|=
0x00000040
*
dll_off
;
break
;
default:
t
->
mr
[
1
]
&=
~
0x00000001
;
t
->
mr
[
1
]
|=
0x00000001
*
dll_off
;
break
;
}
}
return
ret
;
}
void
nouveau_mem_timing_read
(
struct
drm_device
*
dev
,
struct
nouveau_pm_memtiming
*
t
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_fb
*
pfb
=
nouveau_fb
(
device
);
u32
timing_base
,
timing_regs
,
mr_base
;
int
i
;
if
(
device
->
card_type
>=
0xC0
)
{
timing_base
=
0x10f290
;
mr_base
=
0x10f300
;
}
else
{
timing_base
=
0x100220
;
mr_base
=
0x1002c0
;
}
t
->
id
=
-
1
;
switch
(
device
->
card_type
)
{
case
NV_50
:
timing_regs
=
9
;
break
;
case
NV_C0
:
case
NV_D0
:
timing_regs
=
5
;
break
;
case
NV_30
:
case
NV_40
:
timing_regs
=
3
;
break
;
default:
timing_regs
=
0
;
return
;
}
for
(
i
=
0
;
i
<
timing_regs
;
i
++
)
t
->
reg
[
i
]
=
nv_rd32
(
device
,
timing_base
+
(
0x04
*
i
));
t
->
tCWL
=
0
;
if
(
device
->
card_type
<
NV_C0
)
{
t
->
tCWL
=
((
nv_rd32
(
device
,
0x100228
)
&
0x0f000000
)
>>
24
)
+
1
;
}
else
if
(
device
->
card_type
<=
NV_D0
)
{
t
->
tCWL
=
((
nv_rd32
(
device
,
0x10f294
)
&
0x00000f80
)
>>
7
);
}
t
->
mr
[
0
]
=
nv_rd32
(
device
,
mr_base
);
t
->
mr
[
1
]
=
nv_rd32
(
device
,
mr_base
+
0x04
);
t
->
mr
[
2
]
=
nv_rd32
(
device
,
mr_base
+
0x20
);
t
->
mr
[
3
]
=
nv_rd32
(
device
,
mr_base
+
0x24
);
t
->
odt
=
0
;
t
->
drive_strength
=
0
;
switch
(
pfb
->
ram
->
type
)
{
case
NV_MEM_TYPE_DDR3
:
t
->
odt
|=
(
t
->
mr
[
1
]
&
0x200
)
>>
7
;
case
NV_MEM_TYPE_DDR2
:
t
->
odt
|=
(
t
->
mr
[
1
]
&
0x04
)
>>
2
|
(
t
->
mr
[
1
]
&
0x40
)
>>
5
;
break
;
case
NV_MEM_TYPE_GDDR3
:
case
NV_MEM_TYPE_GDDR5
:
t
->
drive_strength
=
t
->
mr
[
1
]
&
0x03
;
t
->
odt
=
(
t
->
mr
[
1
]
&
0x0c
)
>>
2
;
break
;
default:
break
;
}
}
int
nouveau_mem_exec
(
struct
nouveau_mem_exec_func
*
exec
,
struct
nouveau_pm_level
*
perflvl
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
exec
->
dev
);
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
struct
nouveau_fb
*
pfb
=
nouveau_fb
(
device
);
struct
nouveau_pm_memtiming
*
info
=
&
perflvl
->
timing
;
u32
tMRD
=
1000
,
tCKSRE
=
0
,
tCKSRX
=
0
,
tXS
=
0
,
tDLLK
=
0
;
u32
mr
[
3
]
=
{
info
->
mr
[
0
],
info
->
mr
[
1
],
info
->
mr
[
2
]
};
u32
mr1_dlloff
;
switch
(
pfb
->
ram
->
type
)
{
case
NV_MEM_TYPE_DDR2
:
tDLLK
=
2000
;
mr1_dlloff
=
0x00000001
;
break
;
case
NV_MEM_TYPE_DDR3
:
tDLLK
=
12000
;
tCKSRE
=
2000
;
tXS
=
1000
;
mr1_dlloff
=
0x00000001
;
break
;
case
NV_MEM_TYPE_GDDR3
:
tDLLK
=
40000
;
mr1_dlloff
=
0x00000040
;
break
;
default:
NV_ERROR
(
drm
,
"cannot reclock unsupported memtype
\n
"
);
return
-
ENODEV
;
}
/* fetch current MRs */
switch
(
pfb
->
ram
->
type
)
{
case
NV_MEM_TYPE_GDDR3
:
case
NV_MEM_TYPE_DDR3
:
mr
[
2
]
=
exec
->
mrg
(
exec
,
2
);
default:
mr
[
1
]
=
exec
->
mrg
(
exec
,
1
);
mr
[
0
]
=
exec
->
mrg
(
exec
,
0
);
break
;
}
/* DLL 'on' -> DLL 'off' mode, disable before entering self-refresh */
if
(
!
(
mr
[
1
]
&
mr1_dlloff
)
&&
(
info
->
mr
[
1
]
&
mr1_dlloff
))
{
exec
->
precharge
(
exec
);
exec
->
mrs
(
exec
,
1
,
mr
[
1
]
|
mr1_dlloff
);
exec
->
wait
(
exec
,
tMRD
);
}
/* enter self-refresh mode */
exec
->
precharge
(
exec
);
exec
->
refresh
(
exec
);
exec
->
refresh
(
exec
);
exec
->
refresh_auto
(
exec
,
false
);
exec
->
refresh_self
(
exec
,
true
);
exec
->
wait
(
exec
,
tCKSRE
);
/* modify input clock frequency */
exec
->
clock_set
(
exec
);
/* exit self-refresh mode */
exec
->
wait
(
exec
,
tCKSRX
);
exec
->
precharge
(
exec
);
exec
->
refresh_self
(
exec
,
false
);
exec
->
refresh_auto
(
exec
,
true
);
exec
->
wait
(
exec
,
tXS
);
exec
->
wait
(
exec
,
tXS
);
/* update MRs */
if
(
mr
[
2
]
!=
info
->
mr
[
2
])
{
exec
->
mrs
(
exec
,
2
,
info
->
mr
[
2
]);
exec
->
wait
(
exec
,
tMRD
);
}
if
(
mr
[
1
]
!=
info
->
mr
[
1
])
{
/* need to keep DLL off until later, at least on GDDR3 */
exec
->
mrs
(
exec
,
1
,
info
->
mr
[
1
]
|
(
mr
[
1
]
&
mr1_dlloff
));
exec
->
wait
(
exec
,
tMRD
);
}
if
(
mr
[
0
]
!=
info
->
mr
[
0
])
{
exec
->
mrs
(
exec
,
0
,
info
->
mr
[
0
]);
exec
->
wait
(
exec
,
tMRD
);
}
/* update PFB timing registers */
exec
->
timing_set
(
exec
);
/* DLL (enable + ) reset */
if
(
!
(
info
->
mr
[
1
]
&
mr1_dlloff
))
{
if
(
mr
[
1
]
&
mr1_dlloff
)
{
exec
->
mrs
(
exec
,
1
,
info
->
mr
[
1
]);
exec
->
wait
(
exec
,
tMRD
);
}
exec
->
mrs
(
exec
,
0
,
info
->
mr
[
0
]
|
0x00000100
);
exec
->
wait
(
exec
,
tMRD
);
exec
->
mrs
(
exec
,
0
,
info
->
mr
[
0
]
|
0x00000000
);
exec
->
wait
(
exec
,
tMRD
);
exec
->
wait
(
exec
,
tDLLK
);
if
(
pfb
->
ram
->
type
==
NV_MEM_TYPE_GDDR3
)
exec
->
precharge
(
exec
);
}
return
0
;
}
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nouveau_perf.c
已删除
100644 → 0
浏览文件 @
c52f4fa6
/*
* 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
*/
#include <drm/drmP.h>
#include "nouveau_drm.h"
#include "nouveau_reg.h"
#include "nouveau_pm.h"
static
u8
*
nouveau_perf_table
(
struct
drm_device
*
dev
,
u8
*
ver
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nvbios
*
bios
=
&
drm
->
vbios
;
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
;
}
u8
*
nouveau_perf_rammap
(
struct
drm_device
*
dev
,
u32
freq
,
u8
*
ver
,
u8
*
hdr
,
u8
*
cnt
,
u8
*
len
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
bit_entry
P
;
u8
*
perf
,
i
=
0
;
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
;
}
if
(
nv_device
(
drm
->
device
)
->
chipset
==
0x49
||
nv_device
(
drm
->
device
)
->
chipset
==
0x4b
)
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
;
}
u8
*
nouveau_perf_ramcfg
(
struct
drm_device
*
dev
,
u32
freq
,
u8
*
ver
,
u8
*
len
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nvbios
*
bios
=
&
drm
->
vbios
;
u8
strap
,
hdr
,
cnt
;
u8
*
rammap
;
strap
=
(
nv_rd32
(
device
,
0x101000
)
&
0x0000003c
)
>>
2
;
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
;
}
u8
*
nouveau_perf_timing
(
struct
drm_device
*
dev
,
u32
freq
,
u8
*
ver
,
u8
*
len
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nvbios
*
bios
=
&
drm
->
vbios
;
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
;
}
static
void
legacy_perf_init
(
struct
drm_device
*
dev
)
{
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
);
char
*
perf
,
*
entry
,
*
bmp
=
&
bios
->
data
[
bios
->
offset
];
int
headerlen
,
use_straps
;
if
(
bmp
[
5
]
<
0x5
||
bmp
[
6
]
<
0x14
)
{
NV_DEBUG
(
drm
,
"BMP version too old for perf
\n
"
);
return
;
}
perf
=
ROMPTR
(
dev
,
bmp
[
0x73
]);
if
(
!
perf
)
{
NV_DEBUG
(
drm
,
"No memclock table pointer found.
\n
"
);
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:
NV_WARN
(
drm
,
"Unknown memclock table version %x.
\n
"
,
perf
[
0
]);
return
;
}
entry
=
perf
+
headerlen
;
if
(
use_straps
)
entry
+=
(
nv_rd32
(
device
,
NV_PEXTDEV_BOOT_0
)
&
0x3c
)
>>
1
;
sprintf
(
pm
->
perflvl
[
0
].
name
,
"performance_level_0"
);
pm
->
perflvl
[
0
].
memory
=
ROM16
(
entry
[
0
])
*
20
;
pm
->
nr_perflvl
=
1
;
}
static
void
nouveau_perf_voltage
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
bit_entry
P
;
u8
*
vmap
;
int
id
;
id
=
perflvl
->
volt_min
;
perflvl
->
volt_min
=
0
;
/* boards using voltage table version <0x40 store the voltage
* level directly in the perflvl entry as a multiple of 10mV
*/
if
(
drm
->
pm
->
voltage
.
version
<
0x40
)
{
perflvl
->
volt_min
=
id
*
10000
;
perflvl
->
volt_max
=
perflvl
->
volt_min
;
return
;
}
/* on newer ones, the perflvl stores an index into yet another
* vbios table containing a min/max voltage value for the perflvl
*/
if
(
bit_table
(
dev
,
'P'
,
&
P
)
||
P
.
version
!=
2
||
P
.
length
<
34
)
{
NV_DEBUG
(
drm
,
"where's our volt map table ptr? %d %d
\n
"
,
P
.
version
,
P
.
length
);
return
;
}
vmap
=
ROMPTR
(
dev
,
P
.
data
[
32
]);
if
(
!
vmap
)
{
NV_DEBUG
(
drm
,
"volt map table pointer invalid
\n
"
);
return
;
}
if
(
id
<
vmap
[
3
])
{
vmap
+=
vmap
[
1
]
+
(
vmap
[
2
]
*
id
);
perflvl
->
volt_min
=
ROM32
(
vmap
[
0
]);
perflvl
->
volt_max
=
ROM32
(
vmap
[
4
]);
}
}
void
nouveau_perf_init
(
struct
drm_device
*
dev
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nvbios
*
bios
=
&
drm
->
vbios
;
u8
*
perf
,
ver
,
hdr
,
cnt
,
len
;
int
ret
,
vid
,
i
=
-
1
;
if
(
bios
->
type
==
NVBIOS_BMP
&&
bios
->
data
[
bios
->
offset
+
6
]
<
0x25
)
{
legacy_perf_init
(
dev
);
return
;
}
perf
=
nouveau_perf_table
(
dev
,
&
ver
);
while
((
perf
=
nouveau_perf_entry
(
dev
,
++
i
,
&
ver
,
&
hdr
,
&
cnt
,
&
len
)))
{
struct
nouveau_pm_level
*
perflvl
=
&
pm
->
perflvl
[
pm
->
nr_perflvl
];
if
(
perf
[
0
]
==
0xff
)
continue
;
switch
(
ver
)
{
case
0x12
:
case
0x13
:
case
0x15
:
perflvl
->
fanspeed
=
perf
[
55
];
if
(
hdr
>
56
)
perflvl
->
volt_min
=
perf
[
56
];
perflvl
->
core
=
ROM32
(
perf
[
1
])
*
10
;
perflvl
->
memory
=
ROM32
(
perf
[
5
])
*
20
;
break
;
case
0x21
:
case
0x23
:
case
0x24
:
perflvl
->
fanspeed
=
perf
[
4
];
perflvl
->
volt_min
=
perf
[
5
];
perflvl
->
shader
=
ROM16
(
perf
[
6
])
*
1000
;
perflvl
->
core
=
perflvl
->
shader
;
perflvl
->
core
+=
(
signed
char
)
perf
[
8
]
*
1000
;
if
(
nv_device
(
drm
->
device
)
->
chipset
==
0x49
||
nv_device
(
drm
->
device
)
->
chipset
==
0x4b
)
perflvl
->
memory
=
ROM16
(
perf
[
11
])
*
1000
;
else
perflvl
->
memory
=
ROM16
(
perf
[
11
])
*
2000
;
break
;
case
0x25
:
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
;
break
;
case
0x30
:
perflvl
->
memscript
=
ROM16
(
perf
[
2
]);
case
0x35
:
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
;
break
;
case
0x40
:
#define subent(n) ((ROM16(perf[hdr + (n) * len]) & 0xfff) * 1000)
perflvl
->
fanspeed
=
0
;
/*XXX*/
perflvl
->
volt_min
=
perf
[
2
];
if
(
nv_device
(
drm
->
device
)
->
card_type
==
NV_50
)
{
perflvl
->
core
=
subent
(
0
);
perflvl
->
shader
=
subent
(
1
);
perflvl
->
memory
=
subent
(
2
);
perflvl
->
vdec
=
subent
(
3
);
perflvl
->
unka0
=
subent
(
4
);
}
else
{
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
);
perflvl
->
core
=
perflvl
->
shader
/
2
;
}
break
;
}
/* make sure vid is valid */
nouveau_perf_voltage
(
dev
,
perflvl
);
if
(
pm
->
voltage
.
supported
&&
perflvl
->
volt_min
)
{
vid
=
nouveau_volt_vid_lookup
(
dev
,
perflvl
->
volt_min
);
if
(
vid
<
0
)
{
NV_DEBUG
(
drm
,
"perflvl %d, bad vid
\n
"
,
i
);
continue
;
}
}
/* get the corresponding memory timings */
ret
=
nouveau_mem_timing_calc
(
dev
,
perflvl
->
memory
,
&
perflvl
->
timing
);
if
(
ret
)
{
NV_DEBUG
(
drm
,
"perflvl %d, bad timing: %d
\n
"
,
i
,
ret
);
continue
;
}
snprintf
(
perflvl
->
name
,
sizeof
(
perflvl
->
name
),
"performance_level_%d"
,
i
);
perflvl
->
id
=
i
;
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
);
pm
->
nr_perflvl
++
;
}
}
void
nouveau_perf_fini
(
struct
drm_device
*
dev
)
{
}
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nouveau_pm.h
已删除
100644 → 0
浏览文件 @
c52f4fa6
/*
* 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
*/
#ifndef __NOUVEAU_PM_H__
#define __NOUVEAU_PM_H__
#include <subdev/bios/pll.h>
#include <subdev/clock.h>
struct
nouveau_pm_voltage_level
{
u32
voltage
;
/* microvolts */
u8
vid
;
};
struct
nouveau_pm_voltage
{
bool
supported
;
u8
version
;
u8
vid_mask
;
struct
nouveau_pm_voltage_level
*
level
;
int
nr_level
;
};
/* Exclusive upper limits */
#define NV_MEM_CL_DDR2_MAX 8
#define NV_MEM_WR_DDR2_MAX 9
#define NV_MEM_CL_DDR3_MAX 17
#define NV_MEM_WR_DDR3_MAX 17
#define NV_MEM_CL_GDDR3_MAX 16
#define NV_MEM_WR_GDDR3_MAX 18
#define NV_MEM_CL_GDDR5_MAX 21
#define NV_MEM_WR_GDDR5_MAX 20
struct
nouveau_pm_memtiming
{
int
id
;
u32
reg
[
9
];
u32
mr
[
4
];
u8
tCWL
;
u8
odt
;
u8
drive_strength
;
};
struct
nouveau_pm_tbl_header
{
u8
version
;
u8
header_len
;
u8
entry_cnt
;
u8
entry_len
;
};
struct
nouveau_pm_tbl_entry
{
u8
tWR
;
u8
tWTR
;
u8
tCL
;
u8
tRC
;
u8
empty_4
;
u8
tRFC
;
/* Byte 5 */
u8
empty_6
;
u8
tRAS
;
/* Byte 7 */
u8
empty_8
;
u8
tRP
;
/* Byte 9 */
u8
tRCDRD
;
u8
tRCDWR
;
u8
tRRD
;
u8
tUNK_13
;
u8
RAM_FT1
;
/* 14, a bitmask of random RAM features */
u8
empty_15
;
u8
tUNK_16
;
u8
empty_17
;
u8
tUNK_18
;
u8
tCWL
;
u8
tUNK_20
,
tUNK_21
;
};
struct
nouveau_pm_profile
;
struct
nouveau_pm_profile_func
{
void
(
*
destroy
)(
struct
nouveau_pm_profile
*
);
void
(
*
init
)(
struct
nouveau_pm_profile
*
);
void
(
*
fini
)(
struct
nouveau_pm_profile
*
);
struct
nouveau_pm_level
*
(
*
select
)(
struct
nouveau_pm_profile
*
);
};
struct
nouveau_pm_profile
{
const
struct
nouveau_pm_profile_func
*
func
;
struct
list_head
head
;
char
name
[
8
];
};
#define NOUVEAU_PM_MAX_LEVEL 8
struct
nouveau_pm_level
{
struct
nouveau_pm_profile
profile
;
struct
device_attribute
dev_attr
;
char
name
[
32
];
int
id
;
struct
nouveau_pm_memtiming
timing
;
u32
memory
;
u16
memscript
;
u32
core
;
u32
shader
;
u32
rop
;
u32
copy
;
u32
daemon
;
u32
vdec
;
u32
dom6
;
u32
unka0
;
/* nva3:nvc0 */
u32
hub01
;
/* nvc0- */
u32
hub06
;
/* nvc0- */
u32
hub07
;
/* nvc0- */
u32
volt_min
;
/* microvolts */
u32
volt_max
;
u8
fanspeed
;
};
struct
nouveau_pm_temp_sensor_constants
{
u16
offset_constant
;
s16
offset_mult
;
s16
offset_div
;
s16
slope_mult
;
s16
slope_div
;
};
struct
nouveau_pm_threshold_temp
{
s16
critical
;
s16
down_clock
;
};
struct
nouveau_pm
{
struct
drm_device
*
dev
;
struct
nouveau_pm_voltage
voltage
;
struct
nouveau_pm_level
perflvl
[
NOUVEAU_PM_MAX_LEVEL
];
int
nr_perflvl
;
struct
nouveau_pm_temp_sensor_constants
sensor_constants
;
struct
nouveau_pm_threshold_temp
threshold_temp
;
struct
nouveau_pm_profile
*
profile_ac
;
struct
nouveau_pm_profile
*
profile_dc
;
struct
nouveau_pm_profile
*
profile
;
struct
list_head
profiles
;
struct
nouveau_pm_level
boot
;
struct
nouveau_pm_level
*
cur
;
struct
device
*
hwmon
;
struct
notifier_block
acpi_nb
;
int
(
*
clocks_get
)(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
void
*
(
*
clocks_pre
)(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
int
(
*
clocks_set
)(
struct
drm_device
*
,
void
*
);
int
(
*
voltage_get
)(
struct
drm_device
*
);
int
(
*
voltage_set
)(
struct
drm_device
*
,
int
voltage
);
};
static
inline
struct
nouveau_pm
*
nouveau_pm
(
struct
drm_device
*
dev
)
{
return
nouveau_drm
(
dev
)
->
pm
;
}
struct
nouveau_mem_exec_func
{
struct
drm_device
*
dev
;
void
(
*
precharge
)(
struct
nouveau_mem_exec_func
*
);
void
(
*
refresh
)(
struct
nouveau_mem_exec_func
*
);
void
(
*
refresh_auto
)(
struct
nouveau_mem_exec_func
*
,
bool
);
void
(
*
refresh_self
)(
struct
nouveau_mem_exec_func
*
,
bool
);
void
(
*
wait
)(
struct
nouveau_mem_exec_func
*
,
u32
nsec
);
u32
(
*
mrg
)(
struct
nouveau_mem_exec_func
*
,
int
mr
);
void
(
*
mrs
)(
struct
nouveau_mem_exec_func
*
,
int
mr
,
u32
data
);
void
(
*
clock_set
)(
struct
nouveau_mem_exec_func
*
);
void
(
*
timing_set
)(
struct
nouveau_mem_exec_func
*
);
void
*
priv
;
};
/* nouveau_mem.c */
int
nouveau_mem_exec
(
struct
nouveau_mem_exec_func
*
,
struct
nouveau_pm_level
*
);
/* nouveau_pm.c */
int
nouveau_pm_init
(
struct
drm_device
*
dev
);
void
nouveau_pm_fini
(
struct
drm_device
*
dev
);
void
nouveau_pm_resume
(
struct
drm_device
*
dev
);
extern
const
struct
nouveau_pm_profile_func
nouveau_pm_static_profile_func
;
void
nouveau_pm_trigger
(
struct
drm_device
*
dev
);
/* nouveau_volt.c */
void
nouveau_volt_init
(
struct
drm_device
*
);
void
nouveau_volt_fini
(
struct
drm_device
*
);
int
nouveau_volt_vid_lookup
(
struct
drm_device
*
,
int
voltage
);
int
nouveau_volt_lvl_lookup
(
struct
drm_device
*
,
int
vid
);
int
nouveau_voltage_gpio_get
(
struct
drm_device
*
);
int
nouveau_voltage_gpio_set
(
struct
drm_device
*
,
int
voltage
);
/* nouveau_perf.c */
void
nouveau_perf_init
(
struct
drm_device
*
);
void
nouveau_perf_fini
(
struct
drm_device
*
);
u8
*
nouveau_perf_rammap
(
struct
drm_device
*
,
u32
freq
,
u8
*
ver
,
u8
*
hdr
,
u8
*
cnt
,
u8
*
len
);
u8
*
nouveau_perf_ramcfg
(
struct
drm_device
*
,
u32
freq
,
u8
*
ver
,
u8
*
len
);
u8
*
nouveau_perf_timing
(
struct
drm_device
*
,
u32
freq
,
u8
*
ver
,
u8
*
len
);
/* nouveau_mem.c */
void
nouveau_mem_timing_init
(
struct
drm_device
*
);
void
nouveau_mem_timing_fini
(
struct
drm_device
*
);
/* nv04_pm.c */
int
nv04_pm_clocks_get
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
void
*
nv04_pm_clocks_pre
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
int
nv04_pm_clocks_set
(
struct
drm_device
*
,
void
*
);
/* nv40_pm.c */
int
nv40_pm_clocks_get
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
void
*
nv40_pm_clocks_pre
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
int
nv40_pm_clocks_set
(
struct
drm_device
*
,
void
*
);
int
nv40_pm_pwm_get
(
struct
drm_device
*
,
int
,
u32
*
,
u32
*
);
int
nv40_pm_pwm_set
(
struct
drm_device
*
,
int
,
u32
,
u32
);
/* nv50_pm.c */
int
nv50_pm_clocks_get
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
void
*
nv50_pm_clocks_pre
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
int
nv50_pm_clocks_set
(
struct
drm_device
*
,
void
*
);
int
nv50_pm_pwm_get
(
struct
drm_device
*
,
int
,
u32
*
,
u32
*
);
int
nv50_pm_pwm_set
(
struct
drm_device
*
,
int
,
u32
,
u32
);
/* nva3_pm.c */
int
nva3_pm_clocks_get
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
void
*
nva3_pm_clocks_pre
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
int
nva3_pm_clocks_set
(
struct
drm_device
*
,
void
*
);
/* nvc0_pm.c */
int
nvc0_pm_clocks_get
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
void
*
nvc0_pm_clocks_pre
(
struct
drm_device
*
,
struct
nouveau_pm_level
*
);
int
nvc0_pm_clocks_set
(
struct
drm_device
*
,
void
*
);
/* nouveau_mem.c */
int
nouveau_mem_timing_calc
(
struct
drm_device
*
,
u32
freq
,
struct
nouveau_pm_memtiming
*
);
void
nouveau_mem_timing_read
(
struct
drm_device
*
,
struct
nouveau_pm_memtiming
*
);
static
inline
int
nva3_calc_pll
(
struct
drm_device
*
dev
,
struct
nvbios_pll
*
pll
,
u32
freq
,
int
*
N
,
int
*
fN
,
int
*
M
,
int
*
P
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_clock
*
clk
=
nouveau_clock
(
device
);
struct
nouveau_pll_vals
pv
;
int
ret
;
ret
=
clk
->
pll_calc
(
clk
,
pll
,
freq
,
&
pv
);
*
N
=
pv
.
N1
;
*
M
=
pv
.
M1
;
*
P
=
pv
.
log2P
;
return
ret
;
}
#endif
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nouveau_volt.c
已删除
100644 → 0
浏览文件 @
c52f4fa6
/*
* 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
*/
#include <drm/drmP.h>
#include "nouveau_drm.h"
#include "nouveau_pm.h"
#include <subdev/bios/gpio.h>
#include <subdev/gpio.h>
static
const
enum
dcb_gpio_func_name
vidtag
[]
=
{
0x04
,
0x05
,
0x06
,
0x1a
,
0x73
};
static
int
nr_vidtag
=
sizeof
(
vidtag
)
/
sizeof
(
vidtag
[
0
]);
int
nouveau_voltage_gpio_get
(
struct
drm_device
*
dev
)
{
struct
nouveau_pm_voltage
*
volt
=
&
nouveau_pm
(
dev
)
->
voltage
;
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_gpio
*
gpio
=
nouveau_gpio
(
device
);
u8
vid
=
0
;
int
i
;
for
(
i
=
0
;
i
<
nr_vidtag
;
i
++
)
{
if
(
!
(
volt
->
vid_mask
&
(
1
<<
i
)))
continue
;
vid
|=
gpio
->
get
(
gpio
,
0
,
vidtag
[
i
],
0xff
)
<<
i
;
}
return
nouveau_volt_lvl_lookup
(
dev
,
vid
);
}
int
nouveau_voltage_gpio_set
(
struct
drm_device
*
dev
,
int
voltage
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_gpio
*
gpio
=
nouveau_gpio
(
device
);
struct
nouveau_pm_voltage
*
volt
=
&
nouveau_pm
(
dev
)
->
voltage
;
int
vid
,
i
;
vid
=
nouveau_volt_vid_lookup
(
dev
,
voltage
);
if
(
vid
<
0
)
return
vid
;
for
(
i
=
0
;
i
<
nr_vidtag
;
i
++
)
{
if
(
!
(
volt
->
vid_mask
&
(
1
<<
i
)))
continue
;
gpio
->
set
(
gpio
,
0
,
vidtag
[
i
],
0xff
,
!!
(
vid
&
(
1
<<
i
)));
}
return
0
;
}
int
nouveau_volt_vid_lookup
(
struct
drm_device
*
dev
,
int
voltage
)
{
struct
nouveau_pm_voltage
*
volt
=
&
nouveau_pm
(
dev
)
->
voltage
;
int
i
;
for
(
i
=
0
;
i
<
volt
->
nr_level
;
i
++
)
{
if
(
volt
->
level
[
i
].
voltage
==
voltage
)
return
volt
->
level
[
i
].
vid
;
}
return
-
ENOENT
;
}
int
nouveau_volt_lvl_lookup
(
struct
drm_device
*
dev
,
int
vid
)
{
struct
nouveau_pm_voltage
*
volt
=
&
nouveau_pm
(
dev
)
->
voltage
;
int
i
;
for
(
i
=
0
;
i
<
volt
->
nr_level
;
i
++
)
{
if
(
volt
->
level
[
i
].
vid
==
vid
)
return
volt
->
level
[
i
].
voltage
;
}
return
-
ENOENT
;
}
void
nouveau_volt_init
(
struct
drm_device
*
dev
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_gpio
*
gpio
=
nouveau_gpio
(
drm
->
device
);
struct
nouveau_pm
*
pm
=
nouveau_pm
(
dev
);
struct
nouveau_pm_voltage
*
voltage
=
&
pm
->
voltage
;
struct
nvbios
*
bios
=
&
drm
->
vbios
;
struct
dcb_gpio_func
func
;
struct
bit_entry
P
;
u8
*
volt
=
NULL
,
*
entry
;
int
i
,
headerlen
,
recordlen
,
entries
,
vidmask
,
vidshift
;
if
(
bios
->
type
==
NVBIOS_BIT
)
{
if
(
bit_table
(
dev
,
'P'
,
&
P
))
return
;
if
(
P
.
version
==
1
)
volt
=
ROMPTR
(
dev
,
P
.
data
[
16
]);
else
if
(
P
.
version
==
2
)
volt
=
ROMPTR
(
dev
,
P
.
data
[
12
]);
else
{
NV_WARN
(
drm
,
"unknown volt for BIT P %d
\n
"
,
P
.
version
);
}
}
else
{
if
(
bios
->
data
[
bios
->
offset
+
6
]
<
0x27
)
{
NV_DEBUG
(
drm
,
"BMP version too old for voltage
\n
"
);
return
;
}
volt
=
ROMPTR
(
dev
,
bios
->
data
[
bios
->
offset
+
0x98
]);
}
if
(
!
volt
)
{
NV_DEBUG
(
drm
,
"voltage table pointer invalid
\n
"
);
return
;
}
switch
(
volt
[
0
])
{
case
0x10
:
case
0x11
:
case
0x12
:
headerlen
=
5
;
recordlen
=
volt
[
1
];
entries
=
volt
[
2
];
vidshift
=
0
;
vidmask
=
volt
[
4
];
break
;
case
0x20
:
headerlen
=
volt
[
1
];
recordlen
=
volt
[
3
];
entries
=
volt
[
2
];
vidshift
=
0
;
/* could be vidshift like 0x30? */
vidmask
=
volt
[
5
];
break
;
case
0x30
:
headerlen
=
volt
[
1
];
recordlen
=
volt
[
2
];
entries
=
volt
[
3
];
vidmask
=
volt
[
4
];
/* no longer certain what volt[5] is, if it's related to
* the vid shift then it's definitely not a function of
* how many bits are set.
*
* after looking at a number of nva3+ vbios images, they
* all seem likely to have a static shift of 2.. lets
* go with that for now until proven otherwise.
*/
vidshift
=
2
;
break
;
case
0x40
:
headerlen
=
volt
[
1
];
recordlen
=
volt
[
2
];
entries
=
volt
[
3
];
/* not a clue what the entries are for.. */
vidmask
=
volt
[
11
];
/* guess.. */
vidshift
=
0
;
break
;
default:
NV_WARN
(
drm
,
"voltage table 0x%02x unknown
\n
"
,
volt
[
0
]);
return
;
}
/* validate vid mask */
voltage
->
vid_mask
=
vidmask
;
if
(
!
voltage
->
vid_mask
)
return
;
i
=
0
;
while
(
vidmask
)
{
if
(
i
>
nr_vidtag
)
{
NV_DEBUG
(
drm
,
"vid bit %d unknown
\n
"
,
i
);
return
;
}
if
(
gpio
&&
gpio
->
find
(
gpio
,
0
,
vidtag
[
i
],
0xff
,
&
func
))
{
NV_DEBUG
(
drm
,
"vid bit %d has no gpio tag
\n
"
,
i
);
return
;
}
vidmask
>>=
1
;
i
++
;
}
/* parse vbios entries into common format */
voltage
->
version
=
volt
[
0
];
if
(
voltage
->
version
<
0x40
)
{
voltage
->
nr_level
=
entries
;
voltage
->
level
=
kcalloc
(
entries
,
sizeof
(
*
voltage
->
level
),
GFP_KERNEL
);
if
(
!
voltage
->
level
)
return
;
entry
=
volt
+
headerlen
;
for
(
i
=
0
;
i
<
entries
;
i
++
,
entry
+=
recordlen
)
{
voltage
->
level
[
i
].
voltage
=
entry
[
0
]
*
10000
;
voltage
->
level
[
i
].
vid
=
entry
[
1
]
>>
vidshift
;
}
}
else
{
u32
volt_uv
=
ROM32
(
volt
[
4
]);
s16
step_uv
=
ROM16
(
volt
[
8
]);
u8
vid
;
voltage
->
nr_level
=
voltage
->
vid_mask
+
1
;
voltage
->
level
=
kcalloc
(
voltage
->
nr_level
,
sizeof
(
*
voltage
->
level
),
GFP_KERNEL
);
if
(
!
voltage
->
level
)
return
;
for
(
vid
=
0
;
vid
<=
voltage
->
vid_mask
;
vid
++
)
{
voltage
->
level
[
vid
].
voltage
=
volt_uv
;
voltage
->
level
[
vid
].
vid
=
vid
;
volt_uv
+=
step_uv
;
}
}
voltage
->
supported
=
true
;
}
void
nouveau_volt_fini
(
struct
drm_device
*
dev
)
{
struct
nouveau_pm_voltage
*
volt
=
&
nouveau_pm
(
dev
)
->
voltage
;
kfree
(
volt
->
level
);
}
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nv04_pm.c
已删除
100644 → 0
浏览文件 @
c52f4fa6
/*
* 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
*/
#include <drm/drmP.h>
#include "nouveau_drm.h"
#include "nouveau_reg.h"
#include "dispnv04/hw.h"
#include "nouveau_pm.h"
#include <subdev/bios/pll.h>
#include <subdev/clock.h>
#include <subdev/timer.h>
int
nv04_pm_clocks_get
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
int
ret
;
ret
=
nouveau_hw_get_clock
(
dev
,
PLL_CORE
);
if
(
ret
<
0
)
return
ret
;
perflvl
->
core
=
ret
;
ret
=
nouveau_hw_get_clock
(
dev
,
PLL_MEMORY
);
if
(
ret
<
0
)
return
ret
;
perflvl
->
memory
=
ret
;
return
0
;
}
struct
nv04_pm_clock
{
struct
nvbios_pll
pll
;
struct
nouveau_pll_vals
calc
;
};
struct
nv04_pm_state
{
struct
nv04_pm_clock
core
;
struct
nv04_pm_clock
memory
;
};
static
int
calc_pll
(
struct
drm_device
*
dev
,
u32
id
,
int
khz
,
struct
nv04_pm_clock
*
clk
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_bios
*
bios
=
nouveau_bios
(
device
);
struct
nouveau_clock
*
pclk
=
nouveau_clock
(
device
);
int
ret
;
ret
=
nvbios_pll_parse
(
bios
,
id
,
&
clk
->
pll
);
if
(
ret
)
return
ret
;
ret
=
pclk
->
pll_calc
(
pclk
,
&
clk
->
pll
,
khz
,
&
clk
->
calc
);
if
(
!
ret
)
return
-
EINVAL
;
return
0
;
}
void
*
nv04_pm_clocks_pre
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
struct
nv04_pm_state
*
info
;
int
ret
;
info
=
kzalloc
(
sizeof
(
*
info
),
GFP_KERNEL
);
if
(
!
info
)
return
ERR_PTR
(
-
ENOMEM
);
ret
=
calc_pll
(
dev
,
PLL_CORE
,
perflvl
->
core
,
&
info
->
core
);
if
(
ret
)
goto
error
;
if
(
perflvl
->
memory
)
{
ret
=
calc_pll
(
dev
,
PLL_MEMORY
,
perflvl
->
memory
,
&
info
->
memory
);
if
(
ret
)
goto
error
;
}
return
info
;
error:
kfree
(
info
);
return
ERR_PTR
(
ret
);
}
static
void
prog_pll
(
struct
drm_device
*
dev
,
struct
nv04_pm_clock
*
clk
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_clock
*
pclk
=
nouveau_clock
(
device
);
u32
reg
=
clk
->
pll
.
reg
;
/* thank the insane nouveau_hw_setpll() interface for this */
if
(
device
->
card_type
>=
NV_40
)
reg
+=
4
;
pclk
->
pll_prog
(
pclk
,
reg
,
&
clk
->
calc
);
}
int
nv04_pm_clocks_set
(
struct
drm_device
*
dev
,
void
*
pre_state
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_timer
*
ptimer
=
nouveau_timer
(
device
);
struct
nv04_pm_state
*
state
=
pre_state
;
prog_pll
(
dev
,
&
state
->
core
);
if
(
state
->
memory
.
pll
.
reg
)
{
prog_pll
(
dev
,
&
state
->
memory
);
if
(
device
->
card_type
<
NV_30
)
{
if
(
device
->
card_type
==
NV_20
)
nv_mask
(
device
,
0x1002c4
,
0
,
1
<<
20
);
/* Reset the DLLs */
nv_mask
(
device
,
0x1002c0
,
0
,
1
<<
8
);
}
}
nv_ofuncs
(
ptimer
)
->
init
(
nv_object
(
ptimer
));
kfree
(
state
);
return
0
;
}
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nv40_pm.c
已删除
100644 → 0
浏览文件 @
c52f4fa6
/*
* Copyright 2011 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
*/
#include <drm/drmP.h>
#include "nouveau_drm.h"
#include "nouveau_bios.h"
#include "nouveau_pm.h"
#include "dispnv04/hw.h"
#include <subdev/bios/pll.h>
#include <subdev/clock.h>
#include <subdev/timer.h>
#include <engine/fifo.h>
#define min2(a,b) ((a) < (b) ? (a) : (b))
static
u32
read_pll_1
(
struct
drm_device
*
dev
,
u32
reg
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
ctrl
=
nv_rd32
(
device
,
reg
+
0x00
);
int
P
=
(
ctrl
&
0x00070000
)
>>
16
;
int
N
=
(
ctrl
&
0x0000ff00
)
>>
8
;
int
M
=
(
ctrl
&
0x000000ff
)
>>
0
;
u32
ref
=
27000
,
clk
=
0
;
if
(
ctrl
&
0x80000000
)
clk
=
ref
*
N
/
M
;
return
clk
>>
P
;
}
static
u32
read_pll_2
(
struct
drm_device
*
dev
,
u32
reg
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
ctrl
=
nv_rd32
(
device
,
reg
+
0x00
);
u32
coef
=
nv_rd32
(
device
,
reg
+
0x04
);
int
N2
=
(
coef
&
0xff000000
)
>>
24
;
int
M2
=
(
coef
&
0x00ff0000
)
>>
16
;
int
N1
=
(
coef
&
0x0000ff00
)
>>
8
;
int
M1
=
(
coef
&
0x000000ff
)
>>
0
;
int
P
=
(
ctrl
&
0x00070000
)
>>
16
;
u32
ref
=
27000
,
clk
=
0
;
if
((
ctrl
&
0x80000000
)
&&
M1
)
{
clk
=
ref
*
N1
/
M1
;
if
((
ctrl
&
0x40000100
)
==
0x40000000
)
{
if
(
M2
)
clk
=
clk
*
N2
/
M2
;
else
clk
=
0
;
}
}
return
clk
>>
P
;
}
static
u32
read_clk
(
struct
drm_device
*
dev
,
u32
src
)
{
switch
(
src
)
{
case
3
:
return
read_pll_2
(
dev
,
0x004000
);
case
2
:
return
read_pll_1
(
dev
,
0x004008
);
default:
break
;
}
return
0
;
}
int
nv40_pm_clocks_get
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
ctrl
=
nv_rd32
(
device
,
0x00c040
);
perflvl
->
core
=
read_clk
(
dev
,
(
ctrl
&
0x00000003
)
>>
0
);
perflvl
->
shader
=
read_clk
(
dev
,
(
ctrl
&
0x00000030
)
>>
4
);
perflvl
->
memory
=
read_pll_2
(
dev
,
0x4020
);
return
0
;
}
struct
nv40_pm_state
{
u32
ctrl
;
u32
npll_ctrl
;
u32
npll_coef
;
u32
spll
;
u32
mpll_ctrl
;
u32
mpll_coef
;
};
static
int
nv40_calc_pll
(
struct
drm_device
*
dev
,
u32
reg
,
struct
nvbios_pll
*
pll
,
u32
clk
,
int
*
N1
,
int
*
M1
,
int
*
N2
,
int
*
M2
,
int
*
log2P
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_bios
*
bios
=
nouveau_bios
(
device
);
struct
nouveau_clock
*
pclk
=
nouveau_clock
(
device
);
struct
nouveau_pll_vals
coef
;
int
ret
;
ret
=
nvbios_pll_parse
(
bios
,
reg
,
pll
);
if
(
ret
)
return
ret
;
if
(
clk
<
pll
->
vco1
.
max_freq
)
pll
->
vco2
.
max_freq
=
0
;
ret
=
pclk
->
pll_calc
(
pclk
,
pll
,
clk
,
&
coef
);
if
(
ret
==
0
)
return
-
ERANGE
;
*
N1
=
coef
.
N1
;
*
M1
=
coef
.
M1
;
if
(
N2
&&
M2
)
{
if
(
pll
->
vco2
.
max_freq
)
{
*
N2
=
coef
.
N2
;
*
M2
=
coef
.
M2
;
}
else
{
*
N2
=
1
;
*
M2
=
1
;
}
}
*
log2P
=
coef
.
log2P
;
return
0
;
}
void
*
nv40_pm_clocks_pre
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
struct
nv40_pm_state
*
info
;
struct
nvbios_pll
pll
;
int
N1
,
N2
,
M1
,
M2
,
log2P
;
int
ret
;
info
=
kmalloc
(
sizeof
(
*
info
),
GFP_KERNEL
);
if
(
!
info
)
return
ERR_PTR
(
-
ENOMEM
);
/* core/geometric clock */
ret
=
nv40_calc_pll
(
dev
,
0x004000
,
&
pll
,
perflvl
->
core
,
&
N1
,
&
M1
,
&
N2
,
&
M2
,
&
log2P
);
if
(
ret
<
0
)
goto
out
;
if
(
N2
==
M2
)
{
info
->
npll_ctrl
=
0x80000100
|
(
log2P
<<
16
);
info
->
npll_coef
=
(
N1
<<
8
)
|
M1
;
}
else
{
info
->
npll_ctrl
=
0xc0000000
|
(
log2P
<<
16
);
info
->
npll_coef
=
(
N2
<<
24
)
|
(
M2
<<
16
)
|
(
N1
<<
8
)
|
M1
;
}
/* use the second PLL for shader/rop clock, if it differs from core */
if
(
perflvl
->
shader
&&
perflvl
->
shader
!=
perflvl
->
core
)
{
ret
=
nv40_calc_pll
(
dev
,
0x004008
,
&
pll
,
perflvl
->
shader
,
&
N1
,
&
M1
,
NULL
,
NULL
,
&
log2P
);
if
(
ret
<
0
)
goto
out
;
info
->
spll
=
0xc0000000
|
(
log2P
<<
16
)
|
(
N1
<<
8
)
|
M1
;
info
->
ctrl
=
0x00000223
;
}
else
{
info
->
spll
=
0x00000000
;
info
->
ctrl
=
0x00000333
;
}
/* memory clock */
if
(
!
perflvl
->
memory
)
{
info
->
mpll_ctrl
=
0x00000000
;
goto
out
;
}
ret
=
nv40_calc_pll
(
dev
,
0x004020
,
&
pll
,
perflvl
->
memory
,
&
N1
,
&
M1
,
&
N2
,
&
M2
,
&
log2P
);
if
(
ret
<
0
)
goto
out
;
info
->
mpll_ctrl
=
0x80000000
|
(
log2P
<<
16
);
info
->
mpll_ctrl
|=
min2
(
pll
.
bias_p
+
log2P
,
pll
.
max_p
)
<<
20
;
if
(
N2
==
M2
)
{
info
->
mpll_ctrl
|=
0x00000100
;
info
->
mpll_coef
=
(
N1
<<
8
)
|
M1
;
}
else
{
info
->
mpll_ctrl
|=
0x40000000
;
info
->
mpll_coef
=
(
N2
<<
24
)
|
(
M2
<<
16
)
|
(
N1
<<
8
)
|
M1
;
}
out:
if
(
ret
<
0
)
{
kfree
(
info
);
info
=
ERR_PTR
(
ret
);
}
return
info
;
}
static
bool
nv40_pm_gr_idle
(
void
*
data
)
{
struct
drm_device
*
dev
=
data
;
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
if
((
nv_rd32
(
device
,
0x400760
)
&
0x000000f0
)
>>
4
!=
(
nv_rd32
(
device
,
0x400760
)
&
0x0000000f
))
return
false
;
if
(
nv_rd32
(
device
,
0x400700
))
return
false
;
return
true
;
}
int
nv40_pm_clocks_set
(
struct
drm_device
*
dev
,
void
*
pre_state
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_fifo
*
pfifo
=
nouveau_fifo
(
device
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nv40_pm_state
*
info
=
pre_state
;
unsigned
long
flags
;
struct
bit_entry
M
;
u32
crtc_mask
=
0
;
u8
sr1
[
2
];
int
i
,
ret
=
-
EAGAIN
;
/* determine which CRTCs are active, fetch VGA_SR1 for each */
for
(
i
=
0
;
i
<
2
;
i
++
)
{
u32
vbl
=
nv_rd32
(
device
,
0x600808
+
(
i
*
0x2000
));
u32
cnt
=
0
;
do
{
if
(
vbl
!=
nv_rd32
(
device
,
0x600808
+
(
i
*
0x2000
)))
{
nv_wr08
(
device
,
0x0c03c4
+
(
i
*
0x2000
),
0x01
);
sr1
[
i
]
=
nv_rd08
(
device
,
0x0c03c5
+
(
i
*
0x2000
));
if
(
!
(
sr1
[
i
]
&
0x20
))
crtc_mask
|=
(
1
<<
i
);
break
;
}
udelay
(
1
);
}
while
(
cnt
++
<
32
);
}
/* halt and idle engines */
pfifo
->
pause
(
pfifo
,
&
flags
);
if
(
!
nv_wait_cb
(
device
,
nv40_pm_gr_idle
,
dev
))
goto
resume
;
ret
=
0
;
/* set engine clocks */
nv_mask
(
device
,
0x00c040
,
0x00000333
,
0x00000000
);
nv_wr32
(
device
,
0x004004
,
info
->
npll_coef
);
nv_mask
(
device
,
0x004000
,
0xc0070100
,
info
->
npll_ctrl
);
nv_mask
(
device
,
0x004008
,
0xc007ffff
,
info
->
spll
);
mdelay
(
5
);
nv_mask
(
device
,
0x00c040
,
0x00000333
,
info
->
ctrl
);
if
(
!
info
->
mpll_ctrl
)
goto
resume
;
/* wait for vblank start on active crtcs, disable memory access */
for
(
i
=
0
;
i
<
2
;
i
++
)
{
if
(
!
(
crtc_mask
&
(
1
<<
i
)))
continue
;
nv_wait
(
device
,
0x600808
+
(
i
*
0x2000
),
0x00010000
,
0x00000000
);
nv_wait
(
device
,
0x600808
+
(
i
*
0x2000
),
0x00010000
,
0x00010000
);
nv_wr08
(
device
,
0x0c03c4
+
(
i
*
0x2000
),
0x01
);
nv_wr08
(
device
,
0x0c03c5
+
(
i
*
0x2000
),
sr1
[
i
]
|
0x20
);
}
/* prepare ram for reclocking */
nv_wr32
(
device
,
0x1002d4
,
0x00000001
);
/* precharge */
nv_wr32
(
device
,
0x1002d0
,
0x00000001
);
/* refresh */
nv_wr32
(
device
,
0x1002d0
,
0x00000001
);
/* refresh */
nv_mask
(
device
,
0x100210
,
0x80000000
,
0x00000000
);
/* no auto refresh */
nv_wr32
(
device
,
0x1002dc
,
0x00000001
);
/* enable self-refresh */
/* change the PLL of each memory partition */
nv_mask
(
device
,
0x00c040
,
0x0000c000
,
0x00000000
);
switch
(
nv_device
(
drm
->
device
)
->
chipset
)
{
case
0x40
:
case
0x45
:
case
0x41
:
case
0x42
:
case
0x47
:
nv_mask
(
device
,
0x004044
,
0xc0771100
,
info
->
mpll_ctrl
);
nv_mask
(
device
,
0x00402c
,
0xc0771100
,
info
->
mpll_ctrl
);
nv_wr32
(
device
,
0x004048
,
info
->
mpll_coef
);
nv_wr32
(
device
,
0x004030
,
info
->
mpll_coef
);
case
0x43
:
case
0x49
:
case
0x4b
:
nv_mask
(
device
,
0x004038
,
0xc0771100
,
info
->
mpll_ctrl
);
nv_wr32
(
device
,
0x00403c
,
info
->
mpll_coef
);
default:
nv_mask
(
device
,
0x004020
,
0xc0771100
,
info
->
mpll_ctrl
);
nv_wr32
(
device
,
0x004024
,
info
->
mpll_coef
);
break
;
}
udelay
(
100
);
nv_mask
(
device
,
0x00c040
,
0x0000c000
,
0x0000c000
);
/* re-enable normal operation of memory controller */
nv_wr32
(
device
,
0x1002dc
,
0x00000000
);
nv_mask
(
device
,
0x100210
,
0x80000000
,
0x80000000
);
udelay
(
100
);
/* execute memory reset script from vbios */
if
(
!
bit_table
(
dev
,
'M'
,
&
M
))
nouveau_bios_run_init_table
(
dev
,
ROM16
(
M
.
data
[
0
]),
NULL
,
0
);
/* make sure we're in vblank (hopefully the same one as before), and
* then re-enable crtc memory access
*/
for
(
i
=
0
;
i
<
2
;
i
++
)
{
if
(
!
(
crtc_mask
&
(
1
<<
i
)))
continue
;
nv_wait
(
device
,
0x600808
+
(
i
*
0x2000
),
0x00010000
,
0x00010000
);
nv_wr08
(
device
,
0x0c03c4
+
(
i
*
0x2000
),
0x01
);
nv_wr08
(
device
,
0x0c03c5
+
(
i
*
0x2000
),
sr1
[
i
]);
}
/* resume engines */
resume:
pfifo
->
start
(
pfifo
,
&
flags
);
kfree
(
info
);
return
ret
;
}
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nv50_pm.c
已删除
100644 → 0
浏览文件 @
c52f4fa6
此差异已折叠。
点击以展开。
drivers/gpu/drm/nouveau/nva3_pm.c
已删除
100644 → 0
浏览文件 @
c52f4fa6
/*
* 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
*/
#include <drm/drmP.h>
#include "nouveau_drm.h"
#include "nouveau_bios.h"
#include "nouveau_pm.h"
#include <subdev/bios/pll.h>
#include <subdev/bios.h>
#include <subdev/clock.h>
#include <subdev/timer.h>
#include <subdev/fb.h>
static
u32
read_clk
(
struct
drm_device
*
,
int
,
bool
);
static
u32
read_pll
(
struct
drm_device
*
,
int
,
u32
);
static
u32
read_vco
(
struct
drm_device
*
dev
,
int
clk
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
sctl
=
nv_rd32
(
device
,
0x4120
+
(
clk
*
4
));
if
((
sctl
&
0x00000030
)
!=
0x00000030
)
return
read_pll
(
dev
,
0x41
,
0x00e820
);
return
read_pll
(
dev
,
0x42
,
0x00e8a0
);
}
static
u32
read_clk
(
struct
drm_device
*
dev
,
int
clk
,
bool
ignore_en
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
u32
sctl
,
sdiv
,
sclk
;
/* refclk for the 0xe8xx plls is a fixed frequency */
if
(
clk
>=
0x40
)
{
if
(
nv_device
(
drm
->
device
)
->
chipset
==
0xaf
)
{
/* no joke.. seriously.. sigh.. */
return
nv_rd32
(
device
,
0x00471c
)
*
1000
;
}
return
device
->
crystal
;
}
sctl
=
nv_rd32
(
device
,
0x4120
+
(
clk
*
4
));
if
(
!
ignore_en
&&
!
(
sctl
&
0x00000100
))
return
0
;
switch
(
sctl
&
0x00003000
)
{
case
0x00000000
:
return
device
->
crystal
;
case
0x00002000
:
if
(
sctl
&
0x00000040
)
return
108000
;
return
100000
;
case
0x00003000
:
sclk
=
read_vco
(
dev
,
clk
);
sdiv
=
((
sctl
&
0x003f0000
)
>>
16
)
+
2
;
return
(
sclk
*
2
)
/
sdiv
;
default:
return
0
;
}
}
static
u32
read_pll
(
struct
drm_device
*
dev
,
int
clk
,
u32
pll
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
ctrl
=
nv_rd32
(
device
,
pll
+
0
);
u32
sclk
=
0
,
P
=
1
,
N
=
1
,
M
=
1
;
if
(
!
(
ctrl
&
0x00000008
))
{
if
(
ctrl
&
0x00000001
)
{
u32
coef
=
nv_rd32
(
device
,
pll
+
4
);
M
=
(
coef
&
0x000000ff
)
>>
0
;
N
=
(
coef
&
0x0000ff00
)
>>
8
;
P
=
(
coef
&
0x003f0000
)
>>
16
;
/* no post-divider on these.. */
if
((
pll
&
0x00ff00
)
==
0x00e800
)
P
=
1
;
sclk
=
read_clk
(
dev
,
0x00
+
clk
,
false
);
}
}
else
{
sclk
=
read_clk
(
dev
,
0x10
+
clk
,
false
);
}
if
(
M
*
P
)
return
sclk
*
N
/
(
M
*
P
);
return
0
;
}
struct
creg
{
u32
clk
;
u32
pll
;
};
static
int
calc_clk
(
struct
drm_device
*
dev
,
int
clk
,
u32
pll
,
u32
khz
,
struct
creg
*
reg
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_bios
*
bios
=
nouveau_bios
(
device
);
struct
nvbios_pll
limits
;
u32
oclk
,
sclk
,
sdiv
;
int
P
,
N
,
M
,
diff
;
int
ret
;
reg
->
pll
=
0
;
reg
->
clk
=
0
;
if
(
!
khz
)
{
NV_DEBUG
(
drm
,
"no clock for 0x%04x/0x%02x
\n
"
,
pll
,
clk
);
return
0
;
}
switch
(
khz
)
{
case
27000
:
reg
->
clk
=
0x00000100
;
return
khz
;
case
100000
:
reg
->
clk
=
0x00002100
;
return
khz
;
case
108000
:
reg
->
clk
=
0x00002140
;
return
khz
;
default:
sclk
=
read_vco
(
dev
,
clk
);
sdiv
=
min
((
sclk
*
2
)
/
(
khz
-
2999
),
(
u32
)
65
);
/* if the clock has a PLL attached, and we can get a within
* [-2, 3) MHz of a divider, we'll disable the PLL and use
* the divider instead.
*
* divider can go as low as 2, limited here because NVIDIA
* and the VBIOS on my NVA8 seem to prefer using the PLL
* for 810MHz - is there a good reason?
*/
if
(
sdiv
>
4
)
{
oclk
=
(
sclk
*
2
)
/
sdiv
;
diff
=
khz
-
oclk
;
if
(
!
pll
||
(
diff
>=
-
2000
&&
diff
<
3000
))
{
reg
->
clk
=
(((
sdiv
-
2
)
<<
16
)
|
0x00003100
);
return
oclk
;
}
}
if
(
!
pll
)
{
NV_ERROR
(
drm
,
"bad freq %02x: %d %d
\n
"
,
clk
,
khz
,
sclk
);
return
-
ERANGE
;
}
break
;
}
ret
=
nvbios_pll_parse
(
bios
,
pll
,
&
limits
);
if
(
ret
)
return
ret
;
limits
.
refclk
=
read_clk
(
dev
,
clk
-
0x10
,
true
);
if
(
!
limits
.
refclk
)
return
-
EINVAL
;
ret
=
nva3_calc_pll
(
dev
,
&
limits
,
khz
,
&
N
,
NULL
,
&
M
,
&
P
);
if
(
ret
>=
0
)
{
reg
->
clk
=
nv_rd32
(
device
,
0x4120
+
(
clk
*
4
));
reg
->
pll
=
(
P
<<
16
)
|
(
N
<<
8
)
|
M
;
}
return
ret
;
}
static
void
prog_pll
(
struct
drm_device
*
dev
,
int
clk
,
u32
pll
,
struct
creg
*
reg
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
const
u32
src0
=
0x004120
+
(
clk
*
4
);
const
u32
src1
=
0x004160
+
(
clk
*
4
);
const
u32
ctrl
=
pll
+
0
;
const
u32
coef
=
pll
+
4
;
if
(
!
reg
->
clk
&&
!
reg
->
pll
)
{
NV_DEBUG
(
drm
,
"no clock for %02x
\n
"
,
clk
);
return
;
}
if
(
reg
->
pll
)
{
nv_mask
(
device
,
src0
,
0x00000101
,
0x00000101
);
nv_wr32
(
device
,
coef
,
reg
->
pll
);
nv_mask
(
device
,
ctrl
,
0x00000015
,
0x00000015
);
nv_mask
(
device
,
ctrl
,
0x00000010
,
0x00000000
);
nv_wait
(
device
,
ctrl
,
0x00020000
,
0x00020000
);
nv_mask
(
device
,
ctrl
,
0x00000010
,
0x00000010
);
nv_mask
(
device
,
ctrl
,
0x00000008
,
0x00000000
);
nv_mask
(
device
,
src1
,
0x00000100
,
0x00000000
);
nv_mask
(
device
,
src1
,
0x00000001
,
0x00000000
);
}
else
{
nv_mask
(
device
,
src1
,
0x003f3141
,
0x00000101
|
reg
->
clk
);
nv_mask
(
device
,
ctrl
,
0x00000018
,
0x00000018
);
udelay
(
20
);
nv_mask
(
device
,
ctrl
,
0x00000001
,
0x00000000
);
nv_mask
(
device
,
src0
,
0x00000100
,
0x00000000
);
nv_mask
(
device
,
src0
,
0x00000001
,
0x00000000
);
}
}
static
void
prog_clk
(
struct
drm_device
*
dev
,
int
clk
,
struct
creg
*
reg
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
if
(
!
reg
->
clk
)
{
NV_DEBUG
(
drm
,
"no clock for %02x
\n
"
,
clk
);
return
;
}
nv_mask
(
device
,
0x004120
+
(
clk
*
4
),
0x003f3141
,
0x00000101
|
reg
->
clk
);
}
int
nva3_pm_clocks_get
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
perflvl
->
core
=
read_pll
(
dev
,
0x00
,
0x4200
);
perflvl
->
shader
=
read_pll
(
dev
,
0x01
,
0x4220
);
perflvl
->
memory
=
read_pll
(
dev
,
0x02
,
0x4000
);
perflvl
->
unka0
=
read_clk
(
dev
,
0x20
,
false
);
perflvl
->
vdec
=
read_clk
(
dev
,
0x21
,
false
);
perflvl
->
daemon
=
read_clk
(
dev
,
0x25
,
false
);
perflvl
->
copy
=
perflvl
->
core
;
return
0
;
}
struct
nva3_pm_state
{
struct
nouveau_pm_level
*
perflvl
;
struct
creg
nclk
;
struct
creg
sclk
;
struct
creg
vdec
;
struct
creg
unka0
;
struct
creg
mclk
;
u8
*
rammap
;
u8
rammap_ver
;
u8
rammap_len
;
u8
*
ramcfg
;
u8
ramcfg_len
;
u32
r004018
;
u32
r100760
;
};
void
*
nva3_pm_clocks_pre
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
struct
nva3_pm_state
*
info
;
u8
ramcfg_cnt
;
int
ret
;
info
=
kzalloc
(
sizeof
(
*
info
),
GFP_KERNEL
);
if
(
!
info
)
return
ERR_PTR
(
-
ENOMEM
);
ret
=
calc_clk
(
dev
,
0x10
,
0x4200
,
perflvl
->
core
,
&
info
->
nclk
);
if
(
ret
<
0
)
goto
out
;
ret
=
calc_clk
(
dev
,
0x11
,
0x4220
,
perflvl
->
shader
,
&
info
->
sclk
);
if
(
ret
<
0
)
goto
out
;
ret
=
calc_clk
(
dev
,
0x12
,
0x4000
,
perflvl
->
memory
,
&
info
->
mclk
);
if
(
ret
<
0
)
goto
out
;
ret
=
calc_clk
(
dev
,
0x20
,
0x0000
,
perflvl
->
unka0
,
&
info
->
unka0
);
if
(
ret
<
0
)
goto
out
;
ret
=
calc_clk
(
dev
,
0x21
,
0x0000
,
perflvl
->
vdec
,
&
info
->
vdec
);
if
(
ret
<
0
)
goto
out
;
info
->
rammap
=
nouveau_perf_rammap
(
dev
,
perflvl
->
memory
,
&
info
->
rammap_ver
,
&
info
->
rammap_len
,
&
ramcfg_cnt
,
&
info
->
ramcfg_len
);
if
(
info
->
rammap_ver
!=
0x10
||
info
->
rammap_len
<
5
)
info
->
rammap
=
NULL
;
info
->
ramcfg
=
nouveau_perf_ramcfg
(
dev
,
perflvl
->
memory
,
&
info
->
rammap_ver
,
&
info
->
ramcfg_len
);
if
(
info
->
rammap_ver
!=
0x10
)
info
->
ramcfg
=
NULL
;
info
->
perflvl
=
perflvl
;
out:
if
(
ret
<
0
)
{
kfree
(
info
);
info
=
ERR_PTR
(
ret
);
}
return
info
;
}
static
bool
nva3_pm_grcp_idle
(
void
*
data
)
{
struct
drm_device
*
dev
=
data
;
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
if
(
!
(
nv_rd32
(
device
,
0x400304
)
&
0x00000001
))
return
true
;
if
(
nv_rd32
(
device
,
0x400308
)
==
0x0050001c
)
return
true
;
return
false
;
}
static
void
mclk_precharge
(
struct
nouveau_mem_exec_func
*
exec
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
nv_wr32
(
device
,
0x1002d4
,
0x00000001
);
}
static
void
mclk_refresh
(
struct
nouveau_mem_exec_func
*
exec
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
nv_wr32
(
device
,
0x1002d0
,
0x00000001
);
}
static
void
mclk_refresh_auto
(
struct
nouveau_mem_exec_func
*
exec
,
bool
enable
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
nv_wr32
(
device
,
0x100210
,
enable
?
0x80000000
:
0x00000000
);
}
static
void
mclk_refresh_self
(
struct
nouveau_mem_exec_func
*
exec
,
bool
enable
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
nv_wr32
(
device
,
0x1002dc
,
enable
?
0x00000001
:
0x00000000
);
}
static
void
mclk_wait
(
struct
nouveau_mem_exec_func
*
exec
,
u32
nsec
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
volatile
u32
post
=
nv_rd32
(
device
,
0
);
(
void
)
post
;
udelay
((
nsec
+
500
)
/
1000
);
}
static
u32
mclk_mrg
(
struct
nouveau_mem_exec_func
*
exec
,
int
mr
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
if
(
mr
<=
1
)
return
nv_rd32
(
device
,
0x1002c0
+
((
mr
-
0
)
*
4
));
if
(
mr
<=
3
)
return
nv_rd32
(
device
,
0x1002e0
+
((
mr
-
2
)
*
4
));
return
0
;
}
static
void
mclk_mrs
(
struct
nouveau_mem_exec_func
*
exec
,
int
mr
,
u32
data
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
struct
nouveau_fb
*
pfb
=
nouveau_fb
(
device
);
if
(
mr
<=
1
)
{
if
(
pfb
->
ram
->
ranks
>
1
)
nv_wr32
(
device
,
0x1002c8
+
((
mr
-
0
)
*
4
),
data
);
nv_wr32
(
device
,
0x1002c0
+
((
mr
-
0
)
*
4
),
data
);
}
else
if
(
mr
<=
3
)
{
if
(
pfb
->
ram
->
ranks
>
1
)
nv_wr32
(
device
,
0x1002e8
+
((
mr
-
2
)
*
4
),
data
);
nv_wr32
(
device
,
0x1002e0
+
((
mr
-
2
)
*
4
),
data
);
}
}
static
void
mclk_clock_set
(
struct
nouveau_mem_exec_func
*
exec
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
struct
nva3_pm_state
*
info
=
exec
->
priv
;
u32
ctrl
;
ctrl
=
nv_rd32
(
device
,
0x004000
);
if
(
!
(
ctrl
&
0x00000008
)
&&
info
->
mclk
.
pll
)
{
nv_wr32
(
device
,
0x004000
,
(
ctrl
|=
0x00000008
));
nv_mask
(
device
,
0x1110e0
,
0x00088000
,
0x00088000
);
nv_wr32
(
device
,
0x004018
,
0x00001000
);
nv_wr32
(
device
,
0x004000
,
(
ctrl
&=
~
0x00000001
));
nv_wr32
(
device
,
0x004004
,
info
->
mclk
.
pll
);
nv_wr32
(
device
,
0x004000
,
(
ctrl
|=
0x00000001
));
udelay
(
64
);
nv_wr32
(
device
,
0x004018
,
0x00005000
|
info
->
r004018
);
udelay
(
20
);
}
else
if
(
!
info
->
mclk
.
pll
)
{
nv_mask
(
device
,
0x004168
,
0x003f3040
,
info
->
mclk
.
clk
);
nv_wr32
(
device
,
0x004000
,
(
ctrl
|=
0x00000008
));
nv_mask
(
device
,
0x1110e0
,
0x00088000
,
0x00088000
);
nv_wr32
(
device
,
0x004018
,
0x0000d000
|
info
->
r004018
);
}
if
(
info
->
rammap
)
{
if
(
info
->
ramcfg
&&
(
info
->
rammap
[
4
]
&
0x08
))
{
u32
unk5a0
=
(
ROM16
(
info
->
ramcfg
[
5
])
<<
8
)
|
info
->
ramcfg
[
5
];
u32
unk5a4
=
ROM16
(
info
->
ramcfg
[
7
]);
u32
unk804
=
(
info
->
ramcfg
[
9
]
&
0xf0
)
<<
16
|
(
info
->
ramcfg
[
3
]
&
0x0f
)
<<
16
|
(
info
->
ramcfg
[
9
]
&
0x0f
)
|
0x80000000
;
nv_wr32
(
device
,
0x1005a0
,
unk5a0
);
nv_wr32
(
device
,
0x1005a4
,
unk5a4
);
nv_wr32
(
device
,
0x10f804
,
unk804
);
nv_mask
(
device
,
0x10053c
,
0x00001000
,
0x00000000
);
}
else
{
nv_mask
(
device
,
0x10053c
,
0x00001000
,
0x00001000
);
nv_mask
(
device
,
0x10f804
,
0x80000000
,
0x00000000
);
nv_mask
(
device
,
0x100760
,
0x22222222
,
info
->
r100760
);
nv_mask
(
device
,
0x1007a0
,
0x22222222
,
info
->
r100760
);
nv_mask
(
device
,
0x1007e0
,
0x22222222
,
info
->
r100760
);
}
}
if
(
info
->
mclk
.
pll
)
{
nv_mask
(
device
,
0x1110e0
,
0x00088000
,
0x00011000
);
nv_wr32
(
device
,
0x004000
,
(
ctrl
&=
~
0x00000008
));
}
}
static
void
mclk_timing_set
(
struct
nouveau_mem_exec_func
*
exec
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
struct
nva3_pm_state
*
info
=
exec
->
priv
;
struct
nouveau_pm_level
*
perflvl
=
info
->
perflvl
;
int
i
;
for
(
i
=
0
;
i
<
9
;
i
++
)
nv_wr32
(
device
,
0x100220
+
(
i
*
4
),
perflvl
->
timing
.
reg
[
i
]);
if
(
info
->
ramcfg
)
{
u32
data
=
(
info
->
ramcfg
[
2
]
&
0x08
)
?
0x00000000
:
0x00001000
;
nv_mask
(
device
,
0x100200
,
0x00001000
,
data
);
}
if
(
info
->
ramcfg
)
{
u32
unk714
=
nv_rd32
(
device
,
0x100714
)
&
~
0xf0000010
;
u32
unk718
=
nv_rd32
(
device
,
0x100718
)
&
~
0x00000100
;
u32
unk71c
=
nv_rd32
(
device
,
0x10071c
)
&
~
0x00000100
;
if
(
(
info
->
ramcfg
[
2
]
&
0x20
))
unk714
|=
0xf0000000
;
if
(
!
(
info
->
ramcfg
[
2
]
&
0x04
))
unk714
|=
0x00000010
;
nv_wr32
(
device
,
0x100714
,
unk714
);
if
(
info
->
ramcfg
[
2
]
&
0x01
)
unk71c
|=
0x00000100
;
nv_wr32
(
device
,
0x10071c
,
unk71c
);
if
(
info
->
ramcfg
[
2
]
&
0x02
)
unk718
|=
0x00000100
;
nv_wr32
(
device
,
0x100718
,
unk718
);
if
(
info
->
ramcfg
[
2
]
&
0x10
)
nv_wr32
(
device
,
0x111100
,
0x48000000
);
/*XXX*/
}
}
static
void
prog_mem
(
struct
drm_device
*
dev
,
struct
nva3_pm_state
*
info
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_mem_exec_func
exec
=
{
.
dev
=
dev
,
.
precharge
=
mclk_precharge
,
.
refresh
=
mclk_refresh
,
.
refresh_auto
=
mclk_refresh_auto
,
.
refresh_self
=
mclk_refresh_self
,
.
wait
=
mclk_wait
,
.
mrg
=
mclk_mrg
,
.
mrs
=
mclk_mrs
,
.
clock_set
=
mclk_clock_set
,
.
timing_set
=
mclk_timing_set
,
.
priv
=
info
};
u32
ctrl
;
/* XXX: where the fuck does 750MHz come from? */
if
(
info
->
perflvl
->
memory
<=
750000
)
{
info
->
r004018
=
0x10000000
;
info
->
r100760
=
0x22222222
;
}
ctrl
=
nv_rd32
(
device
,
0x004000
);
if
(
ctrl
&
0x00000008
)
{
if
(
info
->
mclk
.
pll
)
{
nv_mask
(
device
,
0x004128
,
0x00000101
,
0x00000101
);
nv_wr32
(
device
,
0x004004
,
info
->
mclk
.
pll
);
nv_wr32
(
device
,
0x004000
,
(
ctrl
|=
0x00000001
));
nv_wr32
(
device
,
0x004000
,
(
ctrl
&=
0xffffffef
));
nv_wait
(
device
,
0x004000
,
0x00020000
,
0x00020000
);
nv_wr32
(
device
,
0x004000
,
(
ctrl
|=
0x00000010
));
nv_wr32
(
device
,
0x004018
,
0x00005000
|
info
->
r004018
);
nv_wr32
(
device
,
0x004000
,
(
ctrl
|=
0x00000004
));
}
}
else
{
u32
ssel
=
0x00000101
;
if
(
info
->
mclk
.
clk
)
ssel
|=
info
->
mclk
.
clk
;
else
ssel
|=
0x00080000
;
/* 324MHz, shouldn't matter... */
nv_mask
(
device
,
0x004168
,
0x003f3141
,
ctrl
);
}
if
(
info
->
ramcfg
)
{
if
(
info
->
ramcfg
[
2
]
&
0x10
)
{
nv_mask
(
device
,
0x111104
,
0x00000600
,
0x00000000
);
}
else
{
nv_mask
(
device
,
0x111100
,
0x40000000
,
0x40000000
);
nv_mask
(
device
,
0x111104
,
0x00000180
,
0x00000000
);
}
}
if
(
info
->
rammap
&&
!
(
info
->
rammap
[
4
]
&
0x02
))
nv_mask
(
device
,
0x100200
,
0x00000800
,
0x00000000
);
nv_wr32
(
device
,
0x611200
,
0x00003300
);
if
(
!
(
info
->
ramcfg
[
2
]
&
0x10
))
nv_wr32
(
device
,
0x111100
,
0x4c020000
);
/*XXX*/
nouveau_mem_exec
(
&
exec
,
info
->
perflvl
);
nv_wr32
(
device
,
0x611200
,
0x00003330
);
if
(
info
->
rammap
&&
(
info
->
rammap
[
4
]
&
0x02
))
nv_mask
(
device
,
0x100200
,
0x00000800
,
0x00000800
);
if
(
info
->
ramcfg
)
{
if
(
info
->
ramcfg
[
2
]
&
0x10
)
{
nv_mask
(
device
,
0x111104
,
0x00000180
,
0x00000180
);
nv_mask
(
device
,
0x111100
,
0x40000000
,
0x00000000
);
}
else
{
nv_mask
(
device
,
0x111104
,
0x00000600
,
0x00000600
);
}
}
if
(
info
->
mclk
.
pll
)
{
nv_mask
(
device
,
0x004168
,
0x00000001
,
0x00000000
);
nv_mask
(
device
,
0x004168
,
0x00000100
,
0x00000000
);
}
else
{
nv_mask
(
device
,
0x004000
,
0x00000001
,
0x00000000
);
nv_mask
(
device
,
0x004128
,
0x00000001
,
0x00000000
);
nv_mask
(
device
,
0x004128
,
0x00000100
,
0x00000000
);
}
}
int
nva3_pm_clocks_set
(
struct
drm_device
*
dev
,
void
*
pre_state
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nva3_pm_state
*
info
=
pre_state
;
int
ret
=
-
EAGAIN
;
/* prevent any new grctx switches from starting */
nv_wr32
(
device
,
0x400324
,
0x00000000
);
nv_wr32
(
device
,
0x400328
,
0x0050001c
);
/* wait flag 0x1c */
/* wait for any pending grctx switches to complete */
if
(
!
nv_wait_cb
(
device
,
nva3_pm_grcp_idle
,
dev
))
{
NV_ERROR
(
drm
,
"pm: ctxprog didn't go idle
\n
"
);
goto
cleanup
;
}
/* freeze PFIFO */
nv_mask
(
device
,
0x002504
,
0x00000001
,
0x00000001
);
if
(
!
nv_wait
(
device
,
0x002504
,
0x00000010
,
0x00000010
))
{
NV_ERROR
(
drm
,
"pm: fifo didn't go idle
\n
"
);
goto
cleanup
;
}
prog_pll
(
dev
,
0x00
,
0x004200
,
&
info
->
nclk
);
prog_pll
(
dev
,
0x01
,
0x004220
,
&
info
->
sclk
);
prog_clk
(
dev
,
0x20
,
&
info
->
unka0
);
prog_clk
(
dev
,
0x21
,
&
info
->
vdec
);
if
(
info
->
mclk
.
clk
||
info
->
mclk
.
pll
)
prog_mem
(
dev
,
info
);
ret
=
0
;
cleanup:
/* unfreeze PFIFO */
nv_mask
(
device
,
0x002504
,
0x00000001
,
0x00000000
);
/* restore ctxprog to normal */
nv_wr32
(
device
,
0x400324
,
0x00000000
);
nv_wr32
(
device
,
0x400328
,
0x0070009c
);
/* set flag 0x1c */
/* unblock it if necessary */
if
(
nv_rd32
(
device
,
0x400308
)
==
0x0050001c
)
nv_mask
(
device
,
0x400824
,
0x10000000
,
0x10000000
);
kfree
(
info
);
return
ret
;
}
This diff is collapsed.
Click to expand it.
drivers/gpu/drm/nouveau/nvc0_pm.c
已删除
100644 → 0
浏览文件 @
c52f4fa6
/*
* Copyright 2011 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
*/
#include "nouveau_drm.h"
#include "nouveau_bios.h"
#include "nouveau_pm.h"
#include <subdev/bios/pll.h>
#include <subdev/bios.h>
#include <subdev/clock.h>
#include <subdev/timer.h>
#include <subdev/fb.h>
static
u32
read_div
(
struct
drm_device
*
,
int
,
u32
,
u32
);
static
u32
read_pll
(
struct
drm_device
*
,
u32
);
static
u32
read_vco
(
struct
drm_device
*
dev
,
u32
dsrc
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
ssrc
=
nv_rd32
(
device
,
dsrc
);
if
(
!
(
ssrc
&
0x00000100
))
return
read_pll
(
dev
,
0x00e800
);
return
read_pll
(
dev
,
0x00e820
);
}
static
u32
read_pll
(
struct
drm_device
*
dev
,
u32
pll
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
ctrl
=
nv_rd32
(
device
,
pll
+
0
);
u32
coef
=
nv_rd32
(
device
,
pll
+
4
);
u32
P
=
(
coef
&
0x003f0000
)
>>
16
;
u32
N
=
(
coef
&
0x0000ff00
)
>>
8
;
u32
M
=
(
coef
&
0x000000ff
)
>>
0
;
u32
sclk
,
doff
;
if
(
!
(
ctrl
&
0x00000001
))
return
0
;
switch
(
pll
&
0xfff000
)
{
case
0x00e000
:
sclk
=
27000
;
P
=
1
;
break
;
case
0x137000
:
doff
=
(
pll
-
0x137000
)
/
0x20
;
sclk
=
read_div
(
dev
,
doff
,
0x137120
,
0x137140
);
break
;
case
0x132000
:
switch
(
pll
)
{
case
0x132000
:
sclk
=
read_pll
(
dev
,
0x132020
);
break
;
case
0x132020
:
sclk
=
read_div
(
dev
,
0
,
0x137320
,
0x137330
);
break
;
default:
return
0
;
}
break
;
default:
return
0
;
}
return
sclk
*
N
/
M
/
P
;
}
static
u32
read_div
(
struct
drm_device
*
dev
,
int
doff
,
u32
dsrc
,
u32
dctl
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
ssrc
=
nv_rd32
(
device
,
dsrc
+
(
doff
*
4
));
u32
sctl
=
nv_rd32
(
device
,
dctl
+
(
doff
*
4
));
switch
(
ssrc
&
0x00000003
)
{
case
0
:
if
((
ssrc
&
0x00030000
)
!=
0x00030000
)
return
27000
;
return
108000
;
case
2
:
return
100000
;
case
3
:
if
(
sctl
&
0x80000000
)
{
u32
sclk
=
read_vco
(
dev
,
dsrc
+
(
doff
*
4
));
u32
sdiv
=
(
sctl
&
0x0000003f
)
+
2
;
return
(
sclk
*
2
)
/
sdiv
;
}
return
read_vco
(
dev
,
dsrc
+
(
doff
*
4
));
default:
return
0
;
}
}
static
u32
read_mem
(
struct
drm_device
*
dev
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
ssel
=
nv_rd32
(
device
,
0x1373f0
);
if
(
ssel
&
0x00000001
)
return
read_div
(
dev
,
0
,
0x137300
,
0x137310
);
return
read_pll
(
dev
,
0x132000
);
}
static
u32
read_clk
(
struct
drm_device
*
dev
,
int
clk
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
sctl
=
nv_rd32
(
device
,
0x137250
+
(
clk
*
4
));
u32
ssel
=
nv_rd32
(
device
,
0x137100
);
u32
sclk
,
sdiv
;
if
(
ssel
&
(
1
<<
clk
))
{
if
(
clk
<
7
)
sclk
=
read_pll
(
dev
,
0x137000
+
(
clk
*
0x20
));
else
sclk
=
read_pll
(
dev
,
0x1370e0
);
sdiv
=
((
sctl
&
0x00003f00
)
>>
8
)
+
2
;
}
else
{
sclk
=
read_div
(
dev
,
clk
,
0x137160
,
0x1371d0
);
sdiv
=
((
sctl
&
0x0000003f
)
>>
0
)
+
2
;
}
if
(
sctl
&
0x80000000
)
return
(
sclk
*
2
)
/
sdiv
;
return
sclk
;
}
int
nvc0_pm_clocks_get
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
perflvl
->
shader
=
read_clk
(
dev
,
0x00
);
perflvl
->
core
=
perflvl
->
shader
/
2
;
perflvl
->
memory
=
read_mem
(
dev
);
perflvl
->
rop
=
read_clk
(
dev
,
0x01
);
perflvl
->
hub07
=
read_clk
(
dev
,
0x02
);
perflvl
->
hub06
=
read_clk
(
dev
,
0x07
);
perflvl
->
hub01
=
read_clk
(
dev
,
0x08
);
perflvl
->
copy
=
read_clk
(
dev
,
0x09
);
perflvl
->
daemon
=
read_clk
(
dev
,
0x0c
);
perflvl
->
vdec
=
read_clk
(
dev
,
0x0e
);
return
0
;
}
struct
nvc0_pm_clock
{
u32
freq
;
u32
ssel
;
u32
mdiv
;
u32
dsrc
;
u32
ddiv
;
u32
coef
;
};
struct
nvc0_pm_state
{
struct
nouveau_pm_level
*
perflvl
;
struct
nvc0_pm_clock
eng
[
16
];
struct
nvc0_pm_clock
mem
;
};
static
u32
calc_div
(
struct
drm_device
*
dev
,
int
clk
,
u32
ref
,
u32
freq
,
u32
*
ddiv
)
{
u32
div
=
min
((
ref
*
2
)
/
freq
,
(
u32
)
65
);
if
(
div
<
2
)
div
=
2
;
*
ddiv
=
div
-
2
;
return
(
ref
*
2
)
/
div
;
}
static
u32
calc_src
(
struct
drm_device
*
dev
,
int
clk
,
u32
freq
,
u32
*
dsrc
,
u32
*
ddiv
)
{
u32
sclk
;
/* use one of the fixed frequencies if possible */
*
ddiv
=
0x00000000
;
switch
(
freq
)
{
case
27000
:
case
108000
:
*
dsrc
=
0x00000000
;
if
(
freq
==
108000
)
*
dsrc
|=
0x00030000
;
return
freq
;
case
100000
:
*
dsrc
=
0x00000002
;
return
freq
;
default:
*
dsrc
=
0x00000003
;
break
;
}
/* otherwise, calculate the closest divider */
sclk
=
read_vco
(
dev
,
clk
);
if
(
clk
<
7
)
sclk
=
calc_div
(
dev
,
clk
,
sclk
,
freq
,
ddiv
);
return
sclk
;
}
static
u32
calc_pll
(
struct
drm_device
*
dev
,
int
clk
,
u32
freq
,
u32
*
coef
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_bios
*
bios
=
nouveau_bios
(
device
);
struct
nvbios_pll
limits
;
int
N
,
M
,
P
,
ret
;
ret
=
nvbios_pll_parse
(
bios
,
0x137000
+
(
clk
*
0x20
),
&
limits
);
if
(
ret
)
return
0
;
limits
.
refclk
=
read_div
(
dev
,
clk
,
0x137120
,
0x137140
);
if
(
!
limits
.
refclk
)
return
0
;
ret
=
nva3_calc_pll
(
dev
,
&
limits
,
freq
,
&
N
,
NULL
,
&
M
,
&
P
);
if
(
ret
<=
0
)
return
0
;
*
coef
=
(
P
<<
16
)
|
(
N
<<
8
)
|
M
;
return
ret
;
}
/* A (likely rather simplified and incomplete) view of the clock tree
*
* Key:
*
* S: source select
* D: divider
* P: pll
* F: switch
*
* Engine clocks:
*
* 137250(D) ---- 137100(F0) ---- 137160(S)/1371d0(D) ------------------- ref
* (F1) ---- 1370X0(P) ---- 137120(S)/137140(D) ---- ref
*
* Not all registers exist for all clocks. For example: clocks >= 8 don't
* have their own PLL (all tied to clock 7's PLL when in PLL mode), nor do
* they have the divider at 1371d0, though the source selection at 137160
* still exists. You must use the divider at 137250 for these instead.
*
* Memory clock:
*
* TBD, read_mem() above is likely very wrong...
*
*/
static
int
calc_clk
(
struct
drm_device
*
dev
,
int
clk
,
struct
nvc0_pm_clock
*
info
,
u32
freq
)
{
u32
src0
,
div0
,
div1D
,
div1P
=
0
;
u32
clk0
,
clk1
=
0
;
/* invalid clock domain */
if
(
!
freq
)
return
0
;
/* first possible path, using only dividers */
clk0
=
calc_src
(
dev
,
clk
,
freq
,
&
src0
,
&
div0
);
clk0
=
calc_div
(
dev
,
clk
,
clk0
,
freq
,
&
div1D
);
/* see if we can get any closer using PLLs */
if
(
clk0
!=
freq
&&
(
0x00004387
&
(
1
<<
clk
)))
{
if
(
clk
<
7
)
clk1
=
calc_pll
(
dev
,
clk
,
freq
,
&
info
->
coef
);
else
clk1
=
read_pll
(
dev
,
0x1370e0
);
clk1
=
calc_div
(
dev
,
clk
,
clk1
,
freq
,
&
div1P
);
}
/* select the method which gets closest to target freq */
if
(
abs
((
int
)
freq
-
clk0
)
<=
abs
((
int
)
freq
-
clk1
))
{
info
->
dsrc
=
src0
;
if
(
div0
)
{
info
->
ddiv
|=
0x80000000
;
info
->
ddiv
|=
div0
<<
8
;
info
->
ddiv
|=
div0
;
}
if
(
div1D
)
{
info
->
mdiv
|=
0x80000000
;
info
->
mdiv
|=
div1D
;
}
info
->
ssel
=
0
;
info
->
freq
=
clk0
;
}
else
{
if
(
div1P
)
{
info
->
mdiv
|=
0x80000000
;
info
->
mdiv
|=
div1P
<<
8
;
}
info
->
ssel
=
(
1
<<
clk
);
info
->
freq
=
clk1
;
}
return
0
;
}
static
int
calc_mem
(
struct
drm_device
*
dev
,
struct
nvc0_pm_clock
*
info
,
u32
freq
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_bios
*
bios
=
nouveau_bios
(
device
);
struct
nvbios_pll
pll
;
int
N
,
M
,
P
,
ret
;
u32
ctrl
;
/* mclk pll input freq comes from another pll, make sure it's on */
ctrl
=
nv_rd32
(
device
,
0x132020
);
if
(
!
(
ctrl
&
0x00000001
))
{
/* if not, program it to 567MHz. nfi where this value comes
* from - it looks like it's in the pll limits table for
* 132000 but the binary driver ignores all my attempts to
* change this value.
*/
nv_wr32
(
device
,
0x137320
,
0x00000103
);
nv_wr32
(
device
,
0x137330
,
0x81200606
);
nv_wait
(
device
,
0x132020
,
0x00010000
,
0x00010000
);
nv_wr32
(
device
,
0x132024
,
0x0001150f
);
nv_mask
(
device
,
0x132020
,
0x00000001
,
0x00000001
);
nv_wait
(
device
,
0x137390
,
0x00020000
,
0x00020000
);
nv_mask
(
device
,
0x132020
,
0x00000004
,
0x00000004
);
}
/* for the moment, until the clock tree is better understood, use
* pll mode for all clock frequencies
*/
ret
=
nvbios_pll_parse
(
bios
,
0x132000
,
&
pll
);
if
(
ret
==
0
)
{
pll
.
refclk
=
read_pll
(
dev
,
0x132020
);
if
(
pll
.
refclk
)
{
ret
=
nva3_calc_pll
(
dev
,
&
pll
,
freq
,
&
N
,
NULL
,
&
M
,
&
P
);
if
(
ret
>
0
)
{
info
->
coef
=
(
P
<<
16
)
|
(
N
<<
8
)
|
M
;
return
0
;
}
}
}
return
-
EINVAL
;
}
void
*
nvc0_pm_clocks_pre
(
struct
drm_device
*
dev
,
struct
nouveau_pm_level
*
perflvl
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nvc0_pm_state
*
info
;
int
ret
;
info
=
kzalloc
(
sizeof
(
*
info
),
GFP_KERNEL
);
if
(
!
info
)
return
ERR_PTR
(
-
ENOMEM
);
/* NFI why this is still in the performance table, the ROPCs appear
* to get their clock from clock 2 ("hub07", actually hub05 on this
* chip, but, anyway...) as well. nvatiming confirms hub05 and ROP
* are always the same freq with the binary driver even when the
* performance table says they should differ.
*/
if
(
device
->
chipset
==
0xd9
)
perflvl
->
rop
=
0
;
if
((
ret
=
calc_clk
(
dev
,
0x00
,
&
info
->
eng
[
0x00
],
perflvl
->
shader
))
||
(
ret
=
calc_clk
(
dev
,
0x01
,
&
info
->
eng
[
0x01
],
perflvl
->
rop
))
||
(
ret
=
calc_clk
(
dev
,
0x02
,
&
info
->
eng
[
0x02
],
perflvl
->
hub07
))
||
(
ret
=
calc_clk
(
dev
,
0x07
,
&
info
->
eng
[
0x07
],
perflvl
->
hub06
))
||
(
ret
=
calc_clk
(
dev
,
0x08
,
&
info
->
eng
[
0x08
],
perflvl
->
hub01
))
||
(
ret
=
calc_clk
(
dev
,
0x09
,
&
info
->
eng
[
0x09
],
perflvl
->
copy
))
||
(
ret
=
calc_clk
(
dev
,
0x0c
,
&
info
->
eng
[
0x0c
],
perflvl
->
daemon
))
||
(
ret
=
calc_clk
(
dev
,
0x0e
,
&
info
->
eng
[
0x0e
],
perflvl
->
vdec
)))
{
kfree
(
info
);
return
ERR_PTR
(
ret
);
}
if
(
perflvl
->
memory
)
{
ret
=
calc_mem
(
dev
,
&
info
->
mem
,
perflvl
->
memory
);
if
(
ret
)
{
kfree
(
info
);
return
ERR_PTR
(
ret
);
}
}
info
->
perflvl
=
perflvl
;
return
info
;
}
static
void
prog_clk
(
struct
drm_device
*
dev
,
int
clk
,
struct
nvc0_pm_clock
*
info
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
/* program dividers at 137160/1371d0 first */
if
(
clk
<
7
&&
!
info
->
ssel
)
{
nv_mask
(
device
,
0x1371d0
+
(
clk
*
0x04
),
0x80003f3f
,
info
->
ddiv
);
nv_wr32
(
device
,
0x137160
+
(
clk
*
0x04
),
info
->
dsrc
);
}
/* switch clock to non-pll mode */
nv_mask
(
device
,
0x137100
,
(
1
<<
clk
),
0x00000000
);
nv_wait
(
device
,
0x137100
,
(
1
<<
clk
),
0x00000000
);
/* reprogram pll */
if
(
clk
<
7
)
{
/* make sure it's disabled first... */
u32
base
=
0x137000
+
(
clk
*
0x20
);
u32
ctrl
=
nv_rd32
(
device
,
base
+
0x00
);
if
(
ctrl
&
0x00000001
)
{
nv_mask
(
device
,
base
+
0x00
,
0x00000004
,
0x00000000
);
nv_mask
(
device
,
base
+
0x00
,
0x00000001
,
0x00000000
);
}
/* program it to new values, if necessary */
if
(
info
->
ssel
)
{
nv_wr32
(
device
,
base
+
0x04
,
info
->
coef
);
nv_mask
(
device
,
base
+
0x00
,
0x00000001
,
0x00000001
);
nv_wait
(
device
,
base
+
0x00
,
0x00020000
,
0x00020000
);
nv_mask
(
device
,
base
+
0x00
,
0x00020004
,
0x00000004
);
}
}
/* select pll/non-pll mode, and program final clock divider */
nv_mask
(
device
,
0x137100
,
(
1
<<
clk
),
info
->
ssel
);
nv_wait
(
device
,
0x137100
,
(
1
<<
clk
),
info
->
ssel
);
nv_mask
(
device
,
0x137250
+
(
clk
*
0x04
),
0x00003f3f
,
info
->
mdiv
);
}
static
void
mclk_precharge
(
struct
nouveau_mem_exec_func
*
exec
)
{
}
static
void
mclk_refresh
(
struct
nouveau_mem_exec_func
*
exec
)
{
}
static
void
mclk_refresh_auto
(
struct
nouveau_mem_exec_func
*
exec
,
bool
enable
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
nv_wr32
(
device
,
0x10f210
,
enable
?
0x80000000
:
0x00000000
);
}
static
void
mclk_refresh_self
(
struct
nouveau_mem_exec_func
*
exec
,
bool
enable
)
{
}
static
void
mclk_wait
(
struct
nouveau_mem_exec_func
*
exec
,
u32
nsec
)
{
udelay
((
nsec
+
500
)
/
1000
);
}
static
u32
mclk_mrg
(
struct
nouveau_mem_exec_func
*
exec
,
int
mr
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
struct
nouveau_fb
*
pfb
=
nouveau_fb
(
device
);
if
(
pfb
->
ram
->
type
!=
NV_MEM_TYPE_GDDR5
)
{
if
(
mr
<=
1
)
return
nv_rd32
(
device
,
0x10f300
+
((
mr
-
0
)
*
4
));
return
nv_rd32
(
device
,
0x10f320
+
((
mr
-
2
)
*
4
));
}
else
{
if
(
mr
==
0
)
return
nv_rd32
(
device
,
0x10f300
+
(
mr
*
4
));
else
if
(
mr
<=
7
)
return
nv_rd32
(
device
,
0x10f32c
+
(
mr
*
4
));
return
nv_rd32
(
device
,
0x10f34c
);
}
}
static
void
mclk_mrs
(
struct
nouveau_mem_exec_func
*
exec
,
int
mr
,
u32
data
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
struct
nouveau_fb
*
pfb
=
nouveau_fb
(
device
);
if
(
pfb
->
ram
->
type
!=
NV_MEM_TYPE_GDDR5
)
{
if
(
mr
<=
1
)
{
nv_wr32
(
device
,
0x10f300
+
((
mr
-
0
)
*
4
),
data
);
if
(
pfb
->
ram
->
ranks
>
1
)
nv_wr32
(
device
,
0x10f308
+
((
mr
-
0
)
*
4
),
data
);
}
else
if
(
mr
<=
3
)
{
nv_wr32
(
device
,
0x10f320
+
((
mr
-
2
)
*
4
),
data
);
if
(
pfb
->
ram
->
ranks
>
1
)
nv_wr32
(
device
,
0x10f328
+
((
mr
-
2
)
*
4
),
data
);
}
}
else
{
if
(
mr
==
0
)
nv_wr32
(
device
,
0x10f300
+
(
mr
*
4
),
data
);
else
if
(
mr
<=
7
)
nv_wr32
(
device
,
0x10f32c
+
(
mr
*
4
),
data
);
else
if
(
mr
==
15
)
nv_wr32
(
device
,
0x10f34c
,
data
);
}
}
static
void
mclk_clock_set
(
struct
nouveau_mem_exec_func
*
exec
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
struct
nvc0_pm_state
*
info
=
exec
->
priv
;
u32
ctrl
=
nv_rd32
(
device
,
0x132000
);
nv_wr32
(
device
,
0x137360
,
0x00000001
);
nv_wr32
(
device
,
0x137370
,
0x00000000
);
nv_wr32
(
device
,
0x137380
,
0x00000000
);
if
(
ctrl
&
0x00000001
)
nv_wr32
(
device
,
0x132000
,
(
ctrl
&=
~
0x00000001
));
nv_wr32
(
device
,
0x132004
,
info
->
mem
.
coef
);
nv_wr32
(
device
,
0x132000
,
(
ctrl
|=
0x00000001
));
nv_wait
(
device
,
0x137390
,
0x00000002
,
0x00000002
);
nv_wr32
(
device
,
0x132018
,
0x00005000
);
nv_wr32
(
device
,
0x137370
,
0x00000001
);
nv_wr32
(
device
,
0x137380
,
0x00000001
);
nv_wr32
(
device
,
0x137360
,
0x00000000
);
}
static
void
mclk_timing_set
(
struct
nouveau_mem_exec_func
*
exec
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
exec
->
dev
);
struct
nvc0_pm_state
*
info
=
exec
->
priv
;
struct
nouveau_pm_level
*
perflvl
=
info
->
perflvl
;
int
i
;
for
(
i
=
0
;
i
<
5
;
i
++
)
nv_wr32
(
device
,
0x10f290
+
(
i
*
4
),
perflvl
->
timing
.
reg
[
i
]);
}
static
void
prog_mem
(
struct
drm_device
*
dev
,
struct
nvc0_pm_state
*
info
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_mem_exec_func
exec
=
{
.
dev
=
dev
,
.
precharge
=
mclk_precharge
,
.
refresh
=
mclk_refresh
,
.
refresh_auto
=
mclk_refresh_auto
,
.
refresh_self
=
mclk_refresh_self
,
.
wait
=
mclk_wait
,
.
mrg
=
mclk_mrg
,
.
mrs
=
mclk_mrs
,
.
clock_set
=
mclk_clock_set
,
.
timing_set
=
mclk_timing_set
,
.
priv
=
info
};
if
(
device
->
chipset
<
0xd0
)
nv_wr32
(
device
,
0x611200
,
0x00003300
);
else
nv_wr32
(
device
,
0x62c000
,
0x03030000
);
nouveau_mem_exec
(
&
exec
,
info
->
perflvl
);
if
(
device
->
chipset
<
0xd0
)
nv_wr32
(
device
,
0x611200
,
0x00003330
);
else
nv_wr32
(
device
,
0x62c000
,
0x03030300
);
}
int
nvc0_pm_clocks_set
(
struct
drm_device
*
dev
,
void
*
data
)
{
struct
nvc0_pm_state
*
info
=
data
;
int
i
;
if
(
info
->
mem
.
coef
)
prog_mem
(
dev
,
info
);
for
(
i
=
0
;
i
<
16
;
i
++
)
{
if
(
!
info
->
eng
[
i
].
freq
)
continue
;
prog_clk
(
dev
,
i
,
&
info
->
eng
[
i
]);
}
kfree
(
info
);
return
0
;
}
This diff is collapsed.
Click to expand it.
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录
反馈
建议
客服
返回
顶部