Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
FengXiao2002
Linux Imx
提交
186ecad2
L
Linux Imx
项目概览
FengXiao2002
/
Linux Imx
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
L
Linux Imx
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
186ecad2
编写于
11月 09, 2012
作者:
B
Ben Skeggs
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
drm/nv50/disp: move remaining interrupt handling into core
Signed-off-by:
N
Ben Skeggs
<
bskeggs@redhat.com
>
上级
4a230fa6
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
461 addition
and
576 deletion
+461
-576
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
+421
-5
drivers/gpu/drm/nouveau/nouveau_irq.c
drivers/gpu/drm/nouveau/nouveau_irq.c
+0
-8
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_display.c
+0
-456
drivers/gpu/drm/nouveau/nv50_display.h
drivers/gpu/drm/nouveau/nv50_display.h
+0
-1
drivers/gpu/drm/nouveau/nv50_sor.c
drivers/gpu/drm/nouveau/nv50_sor.c
+40
-106
未找到文件。
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
浏览文件 @
186ecad2
...
...
@@ -30,9 +30,15 @@
#include <engine/software.h>
#include <engine/disp.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
#include <subdev/bios/disp.h>
#include <subdev/bios/init.h>
#include <subdev/bios/pll.h>
#include <subdev/timer.h>
#include <subdev/fb.h>
#include <subdev/bar.h>
#include <subdev/clock.h>
#include "nv50.h"
...
...
@@ -729,6 +735,27 @@ nv50_disp_cclass = {
* Display engine implementation
******************************************************************************/
static
void
nv50_disp_intr_error
(
struct
nv50_disp_priv
*
priv
)
{
u32
channels
=
(
nv_rd32
(
priv
,
0x610020
)
&
0x001f0000
)
>>
16
;
u32
addr
,
data
;
int
chid
;
for
(
chid
=
0
;
chid
<
5
;
chid
++
)
{
if
(
!
(
channels
&
(
1
<<
chid
)))
continue
;
nv_wr32
(
priv
,
0x610020
,
0x00010000
<<
chid
);
addr
=
nv_rd32
(
priv
,
0x610080
+
(
chid
*
0x08
));
data
=
nv_rd32
(
priv
,
0x610084
+
(
chid
*
0x08
));
nv_wr32
(
priv
,
0x610080
+
(
chid
*
0x08
),
0x90000000
);
nv_error
(
priv
,
"chid %d mthd 0x%04x data 0x%08x 0x%08x
\n
"
,
chid
,
addr
&
0xffc
,
data
,
addr
);
}
}
static
void
nv50_disp_intr_vblank
(
struct
nv50_disp_priv
*
priv
,
int
crtc
)
{
...
...
@@ -766,24 +793,413 @@ nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
disp
->
vblank
.
notify
(
disp
->
vblank
.
data
,
crtc
);
}
static
u16
exec_lookup
(
struct
nv50_disp_priv
*
priv
,
int
head
,
int
outp
,
u32
ctrl
,
struct
dcb_output
*
dcb
,
u8
*
ver
,
u8
*
hdr
,
u8
*
cnt
,
u8
*
len
,
struct
nvbios_outp
*
info
)
{
struct
nouveau_bios
*
bios
=
nouveau_bios
(
priv
);
u16
mask
,
type
,
data
;
if
(
outp
<
4
)
{
type
=
DCB_OUTPUT_ANALOG
;
mask
=
0
;
}
else
{
outp
-=
4
;
switch
(
ctrl
&
0x00000f00
)
{
case
0x00000000
:
type
=
DCB_OUTPUT_LVDS
;
mask
=
1
;
break
;
case
0x00000100
:
type
=
DCB_OUTPUT_TMDS
;
mask
=
1
;
break
;
case
0x00000200
:
type
=
DCB_OUTPUT_TMDS
;
mask
=
2
;
break
;
case
0x00000500
:
type
=
DCB_OUTPUT_TMDS
;
mask
=
3
;
break
;
case
0x00000800
:
type
=
DCB_OUTPUT_DP
;
mask
=
1
;
break
;
case
0x00000900
:
type
=
DCB_OUTPUT_DP
;
mask
=
2
;
break
;
default:
nv_error
(
priv
,
"unknown SOR mc 0x%08x
\n
"
,
ctrl
);
return
0x0000
;
}
}
mask
=
0x00c0
&
(
mask
<<
6
);
mask
|=
0x0001
<<
outp
;
mask
|=
0x0100
<<
head
;
data
=
dcb_outp_match
(
bios
,
type
,
mask
,
ver
,
hdr
,
dcb
);
if
(
!
data
)
return
0x0000
;
return
nvbios_outp_match
(
bios
,
type
,
mask
,
ver
,
hdr
,
cnt
,
len
,
info
);
}
static
bool
exec_script
(
struct
nv50_disp_priv
*
priv
,
int
head
,
int
id
)
{
struct
nouveau_bios
*
bios
=
nouveau_bios
(
priv
);
struct
nvbios_outp
info
;
struct
dcb_output
dcb
;
u8
ver
,
hdr
,
cnt
,
len
;
u16
data
;
u32
ctrl
=
0x00000000
;
int
i
;
for
(
i
=
0
;
!
(
ctrl
&
(
1
<<
head
))
&&
i
<
3
;
i
++
)
ctrl
=
nv_rd32
(
priv
,
0x610b5c
+
(
i
*
8
));
if
(
nv_device
(
priv
)
->
chipset
<
0x90
||
nv_device
(
priv
)
->
chipset
==
0x92
||
nv_device
(
priv
)
->
chipset
==
0xa0
)
{
for
(
i
=
0
;
!
(
ctrl
&
(
1
<<
head
))
&&
i
<
2
;
i
++
)
ctrl
=
nv_rd32
(
priv
,
0x610b74
+
(
i
*
8
));
i
+=
3
;
}
else
{
for
(
i
=
0
;
!
(
ctrl
&
(
1
<<
head
))
&&
i
<
4
;
i
++
)
ctrl
=
nv_rd32
(
priv
,
0x610798
+
(
i
*
8
));
i
+=
3
;
}
if
(
!
(
ctrl
&
(
1
<<
head
)))
return
false
;
data
=
exec_lookup
(
priv
,
head
,
i
,
ctrl
,
&
dcb
,
&
ver
,
&
hdr
,
&
cnt
,
&
len
,
&
info
);
if
(
data
)
{
struct
nvbios_init
init
=
{
.
subdev
=
nv_subdev
(
priv
),
.
bios
=
bios
,
.
offset
=
info
.
script
[
id
],
.
outp
=
&
dcb
,
.
crtc
=
head
,
.
execute
=
1
,
};
return
nvbios_exec
(
&
init
)
==
0
;
}
return
false
;
}
static
u32
exec_clkcmp
(
struct
nv50_disp_priv
*
priv
,
int
head
,
int
id
,
u32
pclk
,
struct
dcb_output
*
outp
)
{
struct
nouveau_bios
*
bios
=
nouveau_bios
(
priv
);
struct
nvbios_outp
info1
;
struct
nvbios_ocfg
info2
;
u8
ver
,
hdr
,
cnt
,
len
;
u16
data
,
conf
;
u32
ctrl
=
0x00000000
;
int
i
;
for
(
i
=
0
;
!
(
ctrl
&
(
1
<<
head
))
&&
i
<
3
;
i
++
)
ctrl
=
nv_rd32
(
priv
,
0x610b58
+
(
i
*
8
));
if
(
nv_device
(
priv
)
->
chipset
<
0x90
||
nv_device
(
priv
)
->
chipset
==
0x92
||
nv_device
(
priv
)
->
chipset
==
0xa0
)
{
for
(
i
=
0
;
!
(
ctrl
&
(
1
<<
head
))
&&
i
<
2
;
i
++
)
ctrl
=
nv_rd32
(
priv
,
0x610b70
+
(
i
*
8
));
i
+=
3
;
}
else
{
for
(
i
=
0
;
!
(
ctrl
&
(
1
<<
head
))
&&
i
<
4
;
i
++
)
ctrl
=
nv_rd32
(
priv
,
0x610794
+
(
i
*
8
));
i
+=
3
;
}
if
(
!
(
ctrl
&
(
1
<<
head
)))
return
0x0000
;
data
=
exec_lookup
(
priv
,
head
,
i
,
ctrl
,
outp
,
&
ver
,
&
hdr
,
&
cnt
,
&
len
,
&
info1
);
if
(
!
data
)
return
0x0000
;
switch
(
outp
->
type
)
{
case
DCB_OUTPUT_TMDS
:
conf
=
(
ctrl
&
0x00000f00
)
>>
8
;
if
(
pclk
>=
165000
)
conf
|=
0x0100
;
break
;
case
DCB_OUTPUT_LVDS
:
conf
=
priv
->
sor
.
lvdsconf
;
break
;
case
DCB_OUTPUT_DP
:
conf
=
(
ctrl
&
0x00000f00
)
>>
8
;
break
;
case
DCB_OUTPUT_ANALOG
:
default:
conf
=
0x00ff
;
break
;
}
data
=
nvbios_ocfg_match
(
bios
,
data
,
conf
,
&
ver
,
&
hdr
,
&
cnt
,
&
len
,
&
info2
);
if
(
data
)
{
data
=
nvbios_oclk_match
(
bios
,
info2
.
clkcmp
[
id
],
pclk
);
if
(
data
)
{
struct
nvbios_init
init
=
{
.
subdev
=
nv_subdev
(
priv
),
.
bios
=
bios
,
.
offset
=
data
,
.
outp
=
outp
,
.
crtc
=
head
,
.
execute
=
1
,
};
if
(
nvbios_exec
(
&
init
))
return
0x0000
;
return
conf
;
}
}
return
0x0000
;
}
static
void
nv50_disp_intr_unk10
(
struct
nv50_disp_priv
*
priv
,
u32
super
)
{
int
head
=
ffs
((
super
&
0x00000060
)
>>
5
)
-
1
;
if
(
head
>=
0
)
{
head
=
ffs
((
super
&
0x00000180
)
>>
7
)
-
1
;
if
(
head
>=
0
)
exec_script
(
priv
,
head
,
1
);
}
nv_wr32
(
priv
,
0x610024
,
0x00000010
);
nv_wr32
(
priv
,
0x610030
,
0x80000000
);
}
static
void
nv50_disp_intr_unk20_dp
(
struct
nv50_disp_priv
*
priv
,
struct
dcb_output
*
outp
,
u32
pclk
)
{
const
int
link
=
!
(
outp
->
sorconf
.
link
&
1
);
const
int
or
=
ffs
(
outp
->
or
)
-
1
;
const
u32
soff
=
(
or
*
0x800
);
const
u32
loff
=
(
link
*
0x080
)
+
soff
;
const
u32
ctrl
=
nv_rd32
(
priv
,
0x610794
+
(
or
*
8
));
const
u32
bits
=
((
ctrl
&
0x000f0000
)
==
0x00020000
)
?
18
:
24
;
const
u32
symbol
=
100000
;
u32
dpctrl
=
nv_rd32
(
priv
,
0x61c10c
+
loff
)
&
0x0000f0000
;
u32
clksor
=
nv_rd32
(
priv
,
0x614300
+
soff
);
int
bestTU
=
0
,
bestVTUi
=
0
,
bestVTUf
=
0
,
bestVTUa
=
0
;
int
TU
,
VTUi
,
VTUf
,
VTUa
;
u64
link_data_rate
,
link_ratio
,
unk
;
u32
best_diff
=
64
*
symbol
;
u32
link_nr
,
link_bw
,
r
;
/* calculate packed data rate for each lane */
if
(
dpctrl
>
0x00030000
)
link_nr
=
4
;
else
if
(
dpctrl
>
0x00010000
)
link_nr
=
2
;
else
link_nr
=
1
;
if
(
clksor
&
0x000c0000
)
link_bw
=
270000
;
else
link_bw
=
162000
;
link_data_rate
=
(
pclk
*
bits
/
8
)
/
link_nr
;
/* calculate ratio of packed data rate to link symbol rate */
link_ratio
=
link_data_rate
*
symbol
;
r
=
do_div
(
link_ratio
,
link_bw
);
for
(
TU
=
64
;
TU
>=
32
;
TU
--
)
{
/* calculate average number of valid symbols in each TU */
u32
tu_valid
=
link_ratio
*
TU
;
u32
calc
,
diff
;
/* find a hw representation for the fraction.. */
VTUi
=
tu_valid
/
symbol
;
calc
=
VTUi
*
symbol
;
diff
=
tu_valid
-
calc
;
if
(
diff
)
{
if
(
diff
>=
(
symbol
/
2
))
{
VTUf
=
symbol
/
(
symbol
-
diff
);
if
(
symbol
-
(
VTUf
*
diff
))
VTUf
++
;
if
(
VTUf
<=
15
)
{
VTUa
=
1
;
calc
+=
symbol
-
(
symbol
/
VTUf
);
}
else
{
VTUa
=
0
;
VTUf
=
1
;
calc
+=
symbol
;
}
}
else
{
VTUa
=
0
;
VTUf
=
min
((
int
)(
symbol
/
diff
),
15
);
calc
+=
symbol
/
VTUf
;
}
diff
=
calc
-
tu_valid
;
}
else
{
/* no remainder, but the hw doesn't like the fractional
* part to be zero. decrement the integer part and
* have the fraction add a whole symbol back
*/
VTUa
=
0
;
VTUf
=
1
;
VTUi
--
;
}
if
(
diff
<
best_diff
)
{
best_diff
=
diff
;
bestTU
=
TU
;
bestVTUa
=
VTUa
;
bestVTUf
=
VTUf
;
bestVTUi
=
VTUi
;
if
(
diff
==
0
)
break
;
}
}
if
(
!
bestTU
)
{
nv_error
(
priv
,
"unable to find suitable dp config
\n
"
);
return
;
}
/* XXX close to vbios numbers, but not right */
unk
=
(
symbol
-
link_ratio
)
*
bestTU
;
unk
*=
link_ratio
;
r
=
do_div
(
unk
,
symbol
);
r
=
do_div
(
unk
,
symbol
);
unk
+=
6
;
nv_mask
(
priv
,
0x61c10c
+
loff
,
0x000001fc
,
bestTU
<<
2
);
nv_mask
(
priv
,
0x61c128
+
loff
,
0x010f7f3f
,
bestVTUa
<<
24
|
bestVTUf
<<
16
|
bestVTUi
<<
8
|
unk
);
}
static
void
nv50_disp_intr_unk20
(
struct
nv50_disp_priv
*
priv
,
u32
super
)
{
struct
dcb_output
outp
;
u32
addr
,
mask
,
data
;
int
head
;
/* finish detaching encoder? */
head
=
ffs
((
super
&
0x00000180
)
>>
7
)
-
1
;
if
(
head
>=
0
)
exec_script
(
priv
,
head
,
2
);
/* check whether a vpll change is required */
head
=
ffs
((
super
&
0x00000600
)
>>
9
)
-
1
;
if
(
head
>=
0
)
{
u32
pclk
=
nv_rd32
(
priv
,
0x610ad0
+
(
head
*
0x540
))
&
0x3fffff
;
if
(
pclk
)
{
struct
nouveau_clock
*
clk
=
nouveau_clock
(
priv
);
clk
->
pll_set
(
clk
,
PLL_VPLL0
+
head
,
pclk
);
}
nv_mask
(
priv
,
0x614200
+
head
*
0x800
,
0x0000000f
,
0x00000000
);
}
/* (re)attach the relevant OR to the head */
head
=
ffs
((
super
&
0x00000180
)
>>
7
)
-
1
;
if
(
head
>=
0
)
{
u32
pclk
=
nv_rd32
(
priv
,
0x610ad0
+
(
head
*
0x540
))
&
0x3fffff
;
u32
conf
=
exec_clkcmp
(
priv
,
head
,
0
,
pclk
,
&
outp
);
if
(
conf
)
{
if
(
outp
.
type
==
DCB_OUTPUT_ANALOG
)
{
addr
=
0x614280
+
(
ffs
(
outp
.
or
)
-
1
)
*
0x800
;
mask
=
0xffffffff
;
data
=
0x00000000
;
}
else
{
if
(
outp
.
type
==
DCB_OUTPUT_DP
)
nv50_disp_intr_unk20_dp
(
priv
,
&
outp
,
pclk
);
addr
=
0x614300
+
(
ffs
(
outp
.
or
)
-
1
)
*
0x800
;
mask
=
0x00000707
;
data
=
(
conf
&
0x0100
)
?
0x0101
:
0x0000
;
}
nv_mask
(
priv
,
addr
,
mask
,
data
);
}
}
nv_wr32
(
priv
,
0x610024
,
0x00000020
);
nv_wr32
(
priv
,
0x610030
,
0x80000000
);
}
/* If programming a TMDS output on a SOR that can also be configured for
* DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off.
*
* It looks like the VBIOS TMDS scripts make an attempt at this, however,
* the VBIOS scripts on at least one board I have only switch it off on
* link 0, causing a blank display if the output has previously been
* programmed for DisplayPort.
*/
static
void
nv50_disp_intr_unk40_tmds
(
struct
nv50_disp_priv
*
priv
,
struct
dcb_output
*
outp
)
{
struct
nouveau_bios
*
bios
=
nouveau_bios
(
priv
);
const
int
link
=
!
(
outp
->
sorconf
.
link
&
1
);
const
int
or
=
ffs
(
outp
->
or
)
-
1
;
const
u32
loff
=
(
or
*
0x800
)
+
(
link
*
0x80
);
const
u16
mask
=
(
outp
->
sorconf
.
link
<<
6
)
|
outp
->
or
;
u8
ver
,
hdr
;
if
(
dcb_outp_match
(
bios
,
DCB_OUTPUT_DP
,
mask
,
&
ver
,
&
hdr
,
outp
))
nv_mask
(
priv
,
0x61c10c
+
loff
,
0x00000001
,
0x00000000
);
}
static
void
nv50_disp_intr_unk40
(
struct
nv50_disp_priv
*
priv
,
u32
super
)
{
int
head
=
ffs
((
super
&
0x00000180
)
>>
7
)
-
1
;
if
(
head
>=
0
)
{
struct
dcb_output
outp
;
u32
pclk
=
nv_rd32
(
priv
,
0x610ad0
+
(
head
*
0x540
))
&
0x3fffff
;
if
(
pclk
&&
exec_clkcmp
(
priv
,
head
,
1
,
pclk
,
&
outp
))
{
if
(
outp
.
type
==
DCB_OUTPUT_TMDS
)
nv50_disp_intr_unk40_tmds
(
priv
,
&
outp
);
}
}
nv_wr32
(
priv
,
0x610024
,
0x00000040
);
nv_wr32
(
priv
,
0x610030
,
0x80000000
);
}
static
void
nv50_disp_intr_super
(
struct
nv50_disp_priv
*
priv
,
u32
intr1
)
{
u32
super
=
nv_rd32
(
priv
,
0x610030
);
nv_debug
(
priv
,
"supervisor 0x%08x 0x%08x
\n
"
,
intr1
,
super
);
if
(
intr1
&
0x00000010
)
nv50_disp_intr_unk10
(
priv
,
super
);
if
(
intr1
&
0x00000020
)
nv50_disp_intr_unk20
(
priv
,
super
);
if
(
intr1
&
0x00000040
)
nv50_disp_intr_unk40
(
priv
,
super
);
}
void
nv50_disp_intr
(
struct
nouveau_subdev
*
subdev
)
{
struct
nv50_disp_priv
*
priv
=
(
void
*
)
subdev
;
u32
stat1
=
nv_rd32
(
priv
,
0x610024
);
u32
intr0
=
nv_rd32
(
priv
,
0x610020
);
u32
intr1
=
nv_rd32
(
priv
,
0x610024
);
if
(
stat1
&
0x00000004
)
{
if
(
intr0
&
0x001f0000
)
{
nv50_disp_intr_error
(
priv
);
intr0
&=
~
0x001f0000
;
}
if
(
intr1
&
0x00000004
)
{
nv50_disp_intr_vblank
(
priv
,
0
);
nv_wr32
(
priv
,
0x610024
,
0x00000004
);
stat
1
&=
~
0x00000004
;
intr
1
&=
~
0x00000004
;
}
if
(
stat
1
&
0x00000008
)
{
if
(
intr
1
&
0x00000008
)
{
nv50_disp_intr_vblank
(
priv
,
1
);
nv_wr32
(
priv
,
0x610024
,
0x00000008
);
stat
1
&=
~
0x00000008
;
intr
1
&=
~
0x00000008
;
}
if
(
intr1
&
0x00000070
)
{
nv50_disp_intr_super
(
priv
,
intr1
);
intr1
&=
~
0x00000070
;
}
}
static
int
...
...
drivers/gpu/drm/nouveau/nouveau_irq.c
浏览文件 @
186ecad2
...
...
@@ -60,14 +60,6 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
return
IRQ_NONE
;
nv_subdev
(
pmc
)
->
intr
(
nv_subdev
(
pmc
));
if
(
dev
->
mode_config
.
num_crtc
&&
device
->
card_type
<=
NV_C0
&&
device
->
card_type
>=
NV_50
)
{
if
(
nv_rd32
(
device
,
0x000100
)
&
0x04000000
)
nv50_display_intr
(
dev
);
}
return
IRQ_HANDLED
;
}
...
...
drivers/gpu/drm/nouveau/nv50_display.c
浏览文件 @
186ecad2
...
...
@@ -40,8 +40,6 @@
#include <subdev/timer.h>
static
void
nv50_display_bh
(
unsigned
long
);
static
inline
int
nv50_sor_nr
(
struct
drm_device
*
dev
)
{
...
...
@@ -312,8 +310,6 @@ nv50_display_create(struct drm_device *dev)
}
}
tasklet_init
(
&
priv
->
tasklet
,
nv50_display_bh
,
(
unsigned
long
)
dev
);
ret
=
nv50_evo_create
(
dev
);
if
(
ret
)
{
nv50_display_destroy
(
dev
);
...
...
@@ -464,455 +460,3 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
dispc
->
sem
.
value
++
;
return
0
;
}
static
u16
nv50_display_script_select
(
struct
drm_device
*
dev
,
struct
dcb_output
*
dcb
,
u32
mc
,
int
pxclk
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nouveau_connector
*
nv_connector
=
NULL
;
struct
drm_encoder
*
encoder
;
struct
nvbios
*
bios
=
&
drm
->
vbios
;
u32
script
=
0
,
or
;
list_for_each_entry
(
encoder
,
&
dev
->
mode_config
.
encoder_list
,
head
)
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
if
(
nv_encoder
->
dcb
!=
dcb
)
continue
;
nv_connector
=
nouveau_encoder_connector_get
(
nv_encoder
);
break
;
}
or
=
ffs
(
dcb
->
or
)
-
1
;
switch
(
dcb
->
type
)
{
case
DCB_OUTPUT_LVDS
:
script
=
(
mc
>>
8
)
&
0xf
;
if
(
bios
->
fp_no_ddc
)
{
if
(
bios
->
fp
.
dual_link
)
script
|=
0x0100
;
if
(
bios
->
fp
.
if_is_24bit
)
script
|=
0x0200
;
}
else
{
/* determine number of lvds links */
if
(
nv_connector
&&
nv_connector
->
edid
&&
nv_connector
->
type
==
DCB_CONNECTOR_LVDS_SPWG
)
{
/* http://www.spwg.org */
if
(((
u8
*
)
nv_connector
->
edid
)[
121
]
==
2
)
script
|=
0x0100
;
}
else
if
(
pxclk
>=
bios
->
fp
.
duallink_transition_clk
)
{
script
|=
0x0100
;
}
/* determine panel depth */
if
(
script
&
0x0100
)
{
if
(
bios
->
fp
.
strapless_is_24bit
&
2
)
script
|=
0x0200
;
}
else
{
if
(
bios
->
fp
.
strapless_is_24bit
&
1
)
script
|=
0x0200
;
}
if
(
nv_connector
&&
nv_connector
->
edid
&&
(
nv_connector
->
edid
->
revision
>=
4
)
&&
(
nv_connector
->
edid
->
input
&
0x70
)
>=
0x20
)
script
|=
0x0200
;
}
break
;
case
DCB_OUTPUT_TMDS
:
script
=
(
mc
>>
8
)
&
0xf
;
if
(
pxclk
>=
165000
)
script
|=
0x0100
;
break
;
case
DCB_OUTPUT_DP
:
script
=
(
mc
>>
8
)
&
0xf
;
break
;
case
DCB_OUTPUT_ANALOG
:
script
=
0xff
;
break
;
default:
NV_ERROR
(
drm
,
"modeset on unsupported output type!
\n
"
);
break
;
}
return
script
;
}
static
void
nv50_display_unk10_handler
(
struct
drm_device
*
dev
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nv50_display
*
disp
=
nv50_display
(
dev
);
u32
unk30
=
nv_rd32
(
device
,
0x610030
),
mc
;
int
i
,
crtc
,
or
=
0
,
type
=
DCB_OUTPUT_ANY
;
NV_DEBUG
(
drm
,
"0x610030: 0x%08x
\n
"
,
unk30
);
disp
->
irq
.
dcb
=
NULL
;
nv_wr32
(
device
,
0x619494
,
nv_rd32
(
device
,
0x619494
)
&
~
8
);
/* Determine which CRTC we're dealing with, only 1 ever will be
* signalled at the same time with the current nouveau code.
*/
crtc
=
ffs
((
unk30
&
0x00000060
)
>>
5
)
-
1
;
if
(
crtc
<
0
)
goto
ack
;
/* Nothing needs to be done for the encoder */
crtc
=
ffs
((
unk30
&
0x00000180
)
>>
7
)
-
1
;
if
(
crtc
<
0
)
goto
ack
;
/* Find which encoder was connected to the CRTC */
for
(
i
=
0
;
type
==
DCB_OUTPUT_ANY
&&
i
<
3
;
i
++
)
{
mc
=
nv_rd32
(
device
,
NV50_PDISPLAY_DAC_MODE_CTRL_C
(
i
));
NV_DEBUG
(
drm
,
"DAC-%d mc: 0x%08x
\n
"
,
i
,
mc
);
if
(
!
(
mc
&
(
1
<<
crtc
)))
continue
;
switch
((
mc
&
0x00000f00
)
>>
8
)
{
case
0
:
type
=
DCB_OUTPUT_ANALOG
;
break
;
case
1
:
type
=
DCB_OUTPUT_TV
;
break
;
default:
NV_ERROR
(
drm
,
"invalid mc, DAC-%d: 0x%08x
\n
"
,
i
,
mc
);
goto
ack
;
}
or
=
i
;
}
for
(
i
=
0
;
type
==
DCB_OUTPUT_ANY
&&
i
<
nv50_sor_nr
(
dev
);
i
++
)
{
if
(
nv_device
(
drm
->
device
)
->
chipset
<
0x90
||
nv_device
(
drm
->
device
)
->
chipset
==
0x92
||
nv_device
(
drm
->
device
)
->
chipset
==
0xa0
)
mc
=
nv_rd32
(
device
,
NV50_PDISPLAY_SOR_MODE_CTRL_C
(
i
));
else
mc
=
nv_rd32
(
device
,
NV90_PDISPLAY_SOR_MODE_CTRL_C
(
i
));
NV_DEBUG
(
drm
,
"SOR-%d mc: 0x%08x
\n
"
,
i
,
mc
);
if
(
!
(
mc
&
(
1
<<
crtc
)))
continue
;
switch
((
mc
&
0x00000f00
)
>>
8
)
{
case
0
:
type
=
DCB_OUTPUT_LVDS
;
break
;
case
1
:
type
=
DCB_OUTPUT_TMDS
;
break
;
case
2
:
type
=
DCB_OUTPUT_TMDS
;
break
;
case
5
:
type
=
DCB_OUTPUT_TMDS
;
break
;
case
8
:
type
=
DCB_OUTPUT_DP
;
break
;
case
9
:
type
=
DCB_OUTPUT_DP
;
break
;
default:
NV_ERROR
(
drm
,
"invalid mc, SOR-%d: 0x%08x
\n
"
,
i
,
mc
);
goto
ack
;
}
or
=
i
;
}
/* There was no encoder to disable */
if
(
type
==
DCB_OUTPUT_ANY
)
goto
ack
;
/* Disable the encoder */
for
(
i
=
0
;
i
<
drm
->
vbios
.
dcb
.
entries
;
i
++
)
{
struct
dcb_output
*
dcb
=
&
drm
->
vbios
.
dcb
.
entry
[
i
];
if
(
dcb
->
type
==
type
&&
(
dcb
->
or
&
(
1
<<
or
)))
{
nouveau_bios_run_display_table
(
dev
,
0
,
-
1
,
dcb
,
-
1
);
disp
->
irq
.
dcb
=
dcb
;
goto
ack
;
}
}
NV_ERROR
(
drm
,
"no dcb for %d %d 0x%08x
\n
"
,
or
,
type
,
mc
);
ack:
nv_wr32
(
device
,
NV50_PDISPLAY_INTR_1
,
NV50_PDISPLAY_INTR_1_CLK_UNK10
);
nv_wr32
(
device
,
0x610030
,
0x80000000
);
}
static
void
nv50_display_unk20_handler
(
struct
drm_device
*
dev
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nv50_display
*
disp
=
nv50_display
(
dev
);
u32
unk30
=
nv_rd32
(
device
,
0x610030
),
tmp
,
pclk
,
script
,
mc
=
0
;
struct
dcb_output
*
dcb
;
int
i
,
crtc
,
or
=
0
,
type
=
DCB_OUTPUT_ANY
;
NV_DEBUG
(
drm
,
"0x610030: 0x%08x
\n
"
,
unk30
);
dcb
=
disp
->
irq
.
dcb
;
if
(
dcb
)
{
nouveau_bios_run_display_table
(
dev
,
0
,
-
2
,
dcb
,
-
1
);
disp
->
irq
.
dcb
=
NULL
;
}
/* CRTC clock change requested? */
crtc
=
ffs
((
unk30
&
0x00000600
)
>>
9
)
-
1
;
if
(
crtc
>=
0
)
{
pclk
=
nv_rd32
(
device
,
NV50_PDISPLAY_CRTC_P
(
crtc
,
CLOCK
));
pclk
&=
0x003fffff
;
if
(
pclk
)
nv50_crtc_set_clock
(
dev
,
crtc
,
pclk
);
tmp
=
nv_rd32
(
device
,
NV50_PDISPLAY_CRTC_CLK_CTRL2
(
crtc
));
tmp
&=
~
0x000000f
;
nv_wr32
(
device
,
NV50_PDISPLAY_CRTC_CLK_CTRL2
(
crtc
),
tmp
);
}
/* Nothing needs to be done for the encoder */
crtc
=
ffs
((
unk30
&
0x00000180
)
>>
7
)
-
1
;
if
(
crtc
<
0
)
goto
ack
;
pclk
=
nv_rd32
(
device
,
NV50_PDISPLAY_CRTC_P
(
crtc
,
CLOCK
))
&
0x003fffff
;
/* Find which encoder is connected to the CRTC */
for
(
i
=
0
;
type
==
DCB_OUTPUT_ANY
&&
i
<
3
;
i
++
)
{
mc
=
nv_rd32
(
device
,
NV50_PDISPLAY_DAC_MODE_CTRL_P
(
i
));
NV_DEBUG
(
drm
,
"DAC-%d mc: 0x%08x
\n
"
,
i
,
mc
);
if
(
!
(
mc
&
(
1
<<
crtc
)))
continue
;
switch
((
mc
&
0x00000f00
)
>>
8
)
{
case
0
:
type
=
DCB_OUTPUT_ANALOG
;
break
;
case
1
:
type
=
DCB_OUTPUT_TV
;
break
;
default:
NV_ERROR
(
drm
,
"invalid mc, DAC-%d: 0x%08x
\n
"
,
i
,
mc
);
goto
ack
;
}
or
=
i
;
}
for
(
i
=
0
;
type
==
DCB_OUTPUT_ANY
&&
i
<
nv50_sor_nr
(
dev
);
i
++
)
{
if
(
nv_device
(
drm
->
device
)
->
chipset
<
0x90
||
nv_device
(
drm
->
device
)
->
chipset
==
0x92
||
nv_device
(
drm
->
device
)
->
chipset
==
0xa0
)
mc
=
nv_rd32
(
device
,
NV50_PDISPLAY_SOR_MODE_CTRL_P
(
i
));
else
mc
=
nv_rd32
(
device
,
NV90_PDISPLAY_SOR_MODE_CTRL_P
(
i
));
NV_DEBUG
(
drm
,
"SOR-%d mc: 0x%08x
\n
"
,
i
,
mc
);
if
(
!
(
mc
&
(
1
<<
crtc
)))
continue
;
switch
((
mc
&
0x00000f00
)
>>
8
)
{
case
0
:
type
=
DCB_OUTPUT_LVDS
;
break
;
case
1
:
type
=
DCB_OUTPUT_TMDS
;
break
;
case
2
:
type
=
DCB_OUTPUT_TMDS
;
break
;
case
5
:
type
=
DCB_OUTPUT_TMDS
;
break
;
case
8
:
type
=
DCB_OUTPUT_DP
;
break
;
case
9
:
type
=
DCB_OUTPUT_DP
;
break
;
default:
NV_ERROR
(
drm
,
"invalid mc, SOR-%d: 0x%08x
\n
"
,
i
,
mc
);
goto
ack
;
}
or
=
i
;
}
if
(
type
==
DCB_OUTPUT_ANY
)
goto
ack
;
/* Enable the encoder */
for
(
i
=
0
;
i
<
drm
->
vbios
.
dcb
.
entries
;
i
++
)
{
dcb
=
&
drm
->
vbios
.
dcb
.
entry
[
i
];
if
(
dcb
->
type
==
type
&&
(
dcb
->
or
&
(
1
<<
or
)))
break
;
}
if
(
i
==
drm
->
vbios
.
dcb
.
entries
)
{
NV_ERROR
(
drm
,
"no dcb for %d %d 0x%08x
\n
"
,
or
,
type
,
mc
);
goto
ack
;
}
script
=
nv50_display_script_select
(
dev
,
dcb
,
mc
,
pclk
);
nouveau_bios_run_display_table
(
dev
,
script
,
pclk
,
dcb
,
-
1
);
if
(
type
==
DCB_OUTPUT_DP
)
{
int
link
=
!
(
dcb
->
dpconf
.
sor
.
link
&
1
);
if
((
mc
&
0x000f0000
)
==
0x00020000
)
nv50_sor_dp_calc_tu
(
dev
,
or
,
link
,
pclk
,
18
);
else
nv50_sor_dp_calc_tu
(
dev
,
or
,
link
,
pclk
,
24
);
}
if
(
dcb
->
type
!=
DCB_OUTPUT_ANALOG
)
{
tmp
=
nv_rd32
(
device
,
NV50_PDISPLAY_SOR_CLK_CTRL2
(
or
));
tmp
&=
~
0x00000f0f
;
if
(
script
&
0x0100
)
tmp
|=
0x00000101
;
nv_wr32
(
device
,
NV50_PDISPLAY_SOR_CLK_CTRL2
(
or
),
tmp
);
}
else
{
nv_wr32
(
device
,
NV50_PDISPLAY_DAC_CLK_CTRL2
(
or
),
0
);
}
disp
->
irq
.
dcb
=
dcb
;
disp
->
irq
.
pclk
=
pclk
;
disp
->
irq
.
script
=
script
;
ack:
nv_wr32
(
device
,
NV50_PDISPLAY_INTR_1
,
NV50_PDISPLAY_INTR_1_CLK_UNK20
);
nv_wr32
(
device
,
0x610030
,
0x80000000
);
}
/* If programming a TMDS output on a SOR that can also be configured for
* DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off.
*
* It looks like the VBIOS TMDS scripts make an attempt at this, however,
* the VBIOS scripts on at least one board I have only switch it off on
* link 0, causing a blank display if the output has previously been
* programmed for DisplayPort.
*/
static
void
nv50_display_unk40_dp_set_tmds
(
struct
drm_device
*
dev
,
struct
dcb_output
*
dcb
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
int
or
=
ffs
(
dcb
->
or
)
-
1
,
link
=
!
(
dcb
->
dpconf
.
sor
.
link
&
1
);
struct
drm_encoder
*
encoder
;
u32
tmp
;
if
(
dcb
->
type
!=
DCB_OUTPUT_TMDS
)
return
;
list_for_each_entry
(
encoder
,
&
dev
->
mode_config
.
encoder_list
,
head
)
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
if
(
nv_encoder
->
dcb
->
type
==
DCB_OUTPUT_DP
&&
nv_encoder
->
dcb
->
or
&
(
1
<<
or
))
{
tmp
=
nv_rd32
(
device
,
NV50_SOR_DP_CTRL
(
or
,
link
));
tmp
&=
~
NV50_SOR_DP_CTRL_ENABLED
;
nv_wr32
(
device
,
NV50_SOR_DP_CTRL
(
or
,
link
),
tmp
);
break
;
}
}
}
static
void
nv50_display_unk40_handler
(
struct
drm_device
*
dev
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nv50_display
*
disp
=
nv50_display
(
dev
);
struct
dcb_output
*
dcb
=
disp
->
irq
.
dcb
;
u16
script
=
disp
->
irq
.
script
;
u32
unk30
=
nv_rd32
(
device
,
0x610030
),
pclk
=
disp
->
irq
.
pclk
;
NV_DEBUG
(
drm
,
"0x610030: 0x%08x
\n
"
,
unk30
);
disp
->
irq
.
dcb
=
NULL
;
if
(
!
dcb
)
goto
ack
;
nouveau_bios_run_display_table
(
dev
,
script
,
-
pclk
,
dcb
,
-
1
);
nv50_display_unk40_dp_set_tmds
(
dev
,
dcb
);
ack:
nv_wr32
(
device
,
NV50_PDISPLAY_INTR_1
,
NV50_PDISPLAY_INTR_1_CLK_UNK40
);
nv_wr32
(
device
,
0x610030
,
0x80000000
);
nv_wr32
(
device
,
0x619494
,
nv_rd32
(
device
,
0x619494
)
|
8
);
}
static
void
nv50_display_bh
(
unsigned
long
data
)
{
struct
drm_device
*
dev
=
(
struct
drm_device
*
)
data
;
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
for
(;;)
{
uint32_t
intr0
=
nv_rd32
(
device
,
NV50_PDISPLAY_INTR_0
);
uint32_t
intr1
=
nv_rd32
(
device
,
NV50_PDISPLAY_INTR_1
);
NV_DEBUG
(
drm
,
"PDISPLAY_INTR_BH 0x%08x 0x%08x
\n
"
,
intr0
,
intr1
);
if
(
intr1
&
NV50_PDISPLAY_INTR_1_CLK_UNK10
)
nv50_display_unk10_handler
(
dev
);
else
if
(
intr1
&
NV50_PDISPLAY_INTR_1_CLK_UNK20
)
nv50_display_unk20_handler
(
dev
);
else
if
(
intr1
&
NV50_PDISPLAY_INTR_1_CLK_UNK40
)
nv50_display_unk40_handler
(
dev
);
else
break
;
}
nv_wr32
(
device
,
NV03_PMC_INTR_EN_0
,
1
);
}
static
void
nv50_display_error_handler
(
struct
drm_device
*
dev
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
u32
channels
=
(
nv_rd32
(
device
,
NV50_PDISPLAY_INTR_0
)
&
0x001f0000
)
>>
16
;
u32
addr
,
data
;
int
chid
;
for
(
chid
=
0
;
chid
<
5
;
chid
++
)
{
if
(
!
(
channels
&
(
1
<<
chid
)))
continue
;
nv_wr32
(
device
,
NV50_PDISPLAY_INTR_0
,
0x00010000
<<
chid
);
addr
=
nv_rd32
(
device
,
NV50_PDISPLAY_TRAPPED_ADDR
(
chid
));
data
=
nv_rd32
(
device
,
NV50_PDISPLAY_TRAPPED_DATA
(
chid
));
NV_ERROR
(
drm
,
"EvoCh %d Mthd 0x%04x Data 0x%08x "
"(0x%04x 0x%02x)
\n
"
,
chid
,
addr
&
0xffc
,
data
,
addr
>>
16
,
(
addr
>>
12
)
&
0xf
);
nv_wr32
(
device
,
NV50_PDISPLAY_TRAPPED_ADDR
(
chid
),
0x90000000
);
}
}
void
nv50_display_intr
(
struct
drm_device
*
dev
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
struct
nv50_display
*
disp
=
nv50_display
(
dev
);
uint32_t
delayed
=
0
;
while
(
nv_rd32
(
device
,
NV50_PMC_INTR_0
)
&
NV50_PMC_INTR_0_DISPLAY
)
{
uint32_t
intr0
=
nv_rd32
(
device
,
NV50_PDISPLAY_INTR_0
);
uint32_t
intr1
=
nv_rd32
(
device
,
NV50_PDISPLAY_INTR_1
);
uint32_t
clock
;
NV_DEBUG
(
drm
,
"PDISPLAY_INTR 0x%08x 0x%08x
\n
"
,
intr0
,
intr1
);
if
(
!
intr0
&&
!
(
intr1
&
~
delayed
))
break
;
if
(
intr0
&
0x001f0000
)
{
nv50_display_error_handler
(
dev
);
intr0
&=
~
0x001f0000
;
}
if
(
intr1
&
NV50_PDISPLAY_INTR_1_VBLANK_CRTC
)
{
intr1
&=
~
NV50_PDISPLAY_INTR_1_VBLANK_CRTC
;
delayed
|=
NV50_PDISPLAY_INTR_1_VBLANK_CRTC
;
}
clock
=
(
intr1
&
(
NV50_PDISPLAY_INTR_1_CLK_UNK10
|
NV50_PDISPLAY_INTR_1_CLK_UNK20
|
NV50_PDISPLAY_INTR_1_CLK_UNK40
));
if
(
clock
)
{
nv_wr32
(
device
,
NV03_PMC_INTR_EN_0
,
0
);
tasklet_schedule
(
&
disp
->
tasklet
);
delayed
|=
clock
;
intr1
&=
~
clock
;
}
if
(
intr0
)
{
NV_ERROR
(
drm
,
"unknown PDISPLAY_INTR_0: 0x%08x
\n
"
,
intr0
);
nv_wr32
(
device
,
NV50_PDISPLAY_INTR_0
,
intr0
);
}
if
(
intr1
)
{
NV_ERROR
(
drm
,
"unknown PDISPLAY_INTR_1: 0x%08x
\n
"
,
intr1
);
nv_wr32
(
device
,
NV50_PDISPLAY_INTR_1
,
intr1
);
}
}
}
drivers/gpu/drm/nouveau/nv50_display.h
浏览文件 @
186ecad2
...
...
@@ -71,7 +71,6 @@ int nv50_display_create(struct drm_device *dev);
int
nv50_display_init
(
struct
drm_device
*
dev
);
void
nv50_display_fini
(
struct
drm_device
*
dev
);
void
nv50_display_destroy
(
struct
drm_device
*
dev
);
void
nv50_display_intr
(
struct
drm_device
*
);
int
nv50_crtc_blank
(
struct
nouveau_crtc
*
,
bool
blank
);
int
nv50_crtc_set_clock
(
struct
drm_device
*
,
int
head
,
int
pclk
);
...
...
drivers/gpu/drm/nouveau/nv50_sor.c
浏览文件 @
186ecad2
...
...
@@ -73,111 +73,6 @@ nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_output *dcb, int crtc,
nv_call
(
disp
->
core
,
NV94_DISP_SOR_DP_LNKCTL
+
moff
,
data
);
}
static
void
nv50_sor_dp_link_get
(
struct
drm_device
*
dev
,
u32
or
,
u32
link
,
u32
*
nr
,
u32
*
bw
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
u32
dpctrl
=
nv_rd32
(
device
,
NV50_SOR_DP_CTRL
(
or
,
link
))
&
0x000f0000
;
u32
clksor
=
nv_rd32
(
device
,
0x614300
+
(
or
*
0x800
));
if
(
clksor
&
0x000c0000
)
*
bw
=
270000
;
else
*
bw
=
162000
;
if
(
dpctrl
>
0x00030000
)
*
nr
=
4
;
else
if
(
dpctrl
>
0x00010000
)
*
nr
=
2
;
else
*
nr
=
1
;
}
void
nv50_sor_dp_calc_tu
(
struct
drm_device
*
dev
,
int
or
,
int
link
,
u32
clk
,
u32
bpp
)
{
struct
nouveau_device
*
device
=
nouveau_dev
(
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
dev
);
const
u32
symbol
=
100000
;
int
bestTU
=
0
,
bestVTUi
=
0
,
bestVTUf
=
0
,
bestVTUa
=
0
;
int
TU
,
VTUi
,
VTUf
,
VTUa
;
u64
link_data_rate
,
link_ratio
,
unk
;
u32
best_diff
=
64
*
symbol
;
u32
link_nr
,
link_bw
,
r
;
/* calculate packed data rate for each lane */
nv50_sor_dp_link_get
(
dev
,
or
,
link
,
&
link_nr
,
&
link_bw
);
link_data_rate
=
(
clk
*
bpp
/
8
)
/
link_nr
;
/* calculate ratio of packed data rate to link symbol rate */
link_ratio
=
link_data_rate
*
symbol
;
r
=
do_div
(
link_ratio
,
link_bw
);
for
(
TU
=
64
;
TU
>=
32
;
TU
--
)
{
/* calculate average number of valid symbols in each TU */
u32
tu_valid
=
link_ratio
*
TU
;
u32
calc
,
diff
;
/* find a hw representation for the fraction.. */
VTUi
=
tu_valid
/
symbol
;
calc
=
VTUi
*
symbol
;
diff
=
tu_valid
-
calc
;
if
(
diff
)
{
if
(
diff
>=
(
symbol
/
2
))
{
VTUf
=
symbol
/
(
symbol
-
diff
);
if
(
symbol
-
(
VTUf
*
diff
))
VTUf
++
;
if
(
VTUf
<=
15
)
{
VTUa
=
1
;
calc
+=
symbol
-
(
symbol
/
VTUf
);
}
else
{
VTUa
=
0
;
VTUf
=
1
;
calc
+=
symbol
;
}
}
else
{
VTUa
=
0
;
VTUf
=
min
((
int
)(
symbol
/
diff
),
15
);
calc
+=
symbol
/
VTUf
;
}
diff
=
calc
-
tu_valid
;
}
else
{
/* no remainder, but the hw doesn't like the fractional
* part to be zero. decrement the integer part and
* have the fraction add a whole symbol back
*/
VTUa
=
0
;
VTUf
=
1
;
VTUi
--
;
}
if
(
diff
<
best_diff
)
{
best_diff
=
diff
;
bestTU
=
TU
;
bestVTUa
=
VTUa
;
bestVTUf
=
VTUf
;
bestVTUi
=
VTUi
;
if
(
diff
==
0
)
break
;
}
}
if
(
!
bestTU
)
{
NV_ERROR
(
drm
,
"DP: unable to find suitable config
\n
"
);
return
;
}
/* XXX close to vbios numbers, but not right */
unk
=
(
symbol
-
link_ratio
)
*
bestTU
;
unk
*=
link_ratio
;
r
=
do_div
(
unk
,
symbol
);
r
=
do_div
(
unk
,
symbol
);
unk
+=
6
;
nv_mask
(
device
,
NV50_SOR_DP_CTRL
(
or
,
link
),
0x000001fc
,
bestTU
<<
2
);
nv_mask
(
device
,
NV50_SOR_DP_SCFG
(
or
,
link
),
0x010f7f3f
,
bestVTUa
<<
24
|
bestVTUf
<<
16
|
bestVTUi
<<
8
|
unk
);
}
static
void
nv50_sor_disconnect
(
struct
drm_encoder
*
encoder
)
{
...
...
@@ -312,7 +207,9 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
struct
nouveau_drm
*
drm
=
nouveau_drm
(
encoder
->
dev
);
struct
nouveau_crtc
*
crtc
=
nouveau_crtc
(
encoder
->
crtc
);
struct
nouveau_connector
*
nv_connector
;
uint32_t
mode_ctl
=
0
;
struct
nv50_display
*
disp
=
nv50_display
(
encoder
->
dev
);
struct
nvbios
*
bios
=
&
drm
->
vbios
;
uint32_t
mode_ctl
=
0
,
script
;
int
ret
;
NV_DEBUG
(
drm
,
"or %d type %d -> crtc %d
\n
"
,
...
...
@@ -331,6 +228,43 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
nouveau_hdmi_mode_set
(
encoder
,
mode
);
break
;
case
DCB_OUTPUT_LVDS
:
script
=
0x0000
;
if
(
bios
->
fp_no_ddc
)
{
if
(
bios
->
fp
.
dual_link
)
script
|=
0x0100
;
if
(
bios
->
fp
.
if_is_24bit
)
script
|=
0x0200
;
}
else
{
/* determine number of lvds links */
nv_connector
=
nouveau_encoder_connector_get
(
nv_encoder
);
if
(
nv_connector
&&
nv_connector
->
edid
&&
nv_connector
->
type
==
DCB_CONNECTOR_LVDS_SPWG
)
{
/* http://www.spwg.org */
if
(((
u8
*
)
nv_connector
->
edid
)[
121
]
==
2
)
script
|=
0x0100
;
}
else
if
(
mode
->
clock
>=
bios
->
fp
.
duallink_transition_clk
)
{
script
|=
0x0100
;
}
/* determine panel depth */
if
(
script
&
0x0100
)
{
if
(
bios
->
fp
.
strapless_is_24bit
&
2
)
script
|=
0x0200
;
}
else
{
if
(
bios
->
fp
.
strapless_is_24bit
&
1
)
script
|=
0x0200
;
}
if
(
nv_connector
&&
nv_connector
->
edid
&&
(
nv_connector
->
edid
->
revision
>=
4
)
&&
(
nv_connector
->
edid
->
input
&
0x70
)
>=
0x20
)
script
|=
0x0200
;
}
nv_call
(
disp
->
core
,
NV50_DISP_SOR_LVDS_SCRIPT
+
nv_encoder
->
or
,
script
);
break
;
case
DCB_OUTPUT_DP
:
nv_connector
=
nouveau_encoder_connector_get
(
nv_encoder
);
if
(
nv_connector
&&
nv_connector
->
base
.
display_info
.
bpc
==
6
)
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录