Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
raspberrypi-kernel
提交
27a45987
R
raspberrypi-kernel
项目概览
openeuler
/
raspberrypi-kernel
通知
13
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
raspberrypi-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
27a45987
编写于
8月 04, 2011
作者:
B
Ben Skeggs
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
drm/nouveau/dp: restructure link training code
Signed-off-by:
N
Ben Skeggs
<
bskeggs@redhat.com
>
上级
a002fece
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
206 addition
and
352 deletion
+206
-352
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nouveau_bios.c
+6
-7
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nouveau_dp.c
+199
-327
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_drv.h
+1
-1
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nouveau_encoder.h
+0
-17
未找到文件。
drivers/gpu/drm/nouveau/nouveau_bios.c
浏览文件 @
27a45987
...
...
@@ -1179,19 +1179,18 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
*
*/
struct
bit_displayport_encoder_table
*
dpe
=
NULL
;
struct
dcb_entry
*
dcb
=
bios
->
display
.
output
;
struct
drm_device
*
dev
=
bios
->
dev
;
uint8_t
cond
=
bios
->
data
[
offset
+
1
];
int
dummy
;
uint8_t
*
table
,
headerlen
;
BIOSLOG
(
bios
,
"0x%04X: subop 0x%02X
\n
"
,
offset
,
cond
);
if
(
!
iexec
->
execute
)
return
3
;
dpe
=
nouveau_bios_dp_table
(
dev
,
dcb
,
&
dummy
);
if
(
!
dp
e
)
{
table
=
nouveau_bios_dp_table
(
dev
,
dcb
,
&
headerlen
);
if
(
!
tabl
e
)
{
NV_ERROR
(
dev
,
"0x%04X: INIT_3A: no encoder table!!
\n
"
,
offset
);
return
3
;
}
...
...
@@ -1208,7 +1207,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
break
;
case
1
:
case
2
:
if
(
!
(
dpe
->
unknown
&
cond
))
if
(
!
(
table
[
5
]
&
cond
))
iexec
->
execute
=
false
;
break
;
case
5
:
...
...
@@ -4480,7 +4479,7 @@ bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
void
*
nouveau_bios_dp_table
(
struct
drm_device
*
dev
,
struct
dcb_entry
*
dcbent
,
int
*
length
)
uint8_t
*
headerlen
)
{
struct
drm_nouveau_private
*
dev_priv
=
dev
->
dev_private
;
struct
nvbios
*
bios
=
&
dev_priv
->
vbios
;
...
...
@@ -4498,7 +4497,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
return
NULL
;
}
*
length
=
table
[
4
];
*
headerlen
=
table
[
4
];
return
bios_output_config_match
(
dev
,
dcbent
,
bios
->
display
.
dp_table_ptr
+
table
[
1
],
table
[
2
],
table
[
3
],
table
[
0
]
>=
0x21
);
...
...
drivers/gpu/drm/nouveau/nouveau_dp.c
浏览文件 @
27a45987
...
...
@@ -28,6 +28,7 @@
#include "nouveau_i2c.h"
#include "nouveau_connector.h"
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
/******************************************************************************
* aux channel util functions
...
...
@@ -178,22 +179,6 @@ auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
return
0
;
}
static
int
auxch_wr
(
struct
drm_encoder
*
encoder
,
int
address
,
uint8_t
*
buf
,
int
size
)
{
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
struct
nouveau_i2c_chan
*
auxch
;
int
ret
;
auxch
=
nouveau_i2c_find
(
dev
,
nv_encoder
->
dcb
->
i2c_index
);
if
(
!
auxch
)
return
-
ENODEV
;
ret
=
nouveau_dp_auxch
(
auxch
,
8
,
address
,
buf
,
size
);
return
ret
;
}
static
u32
dp_link_bw_get
(
struct
drm_device
*
dev
,
int
or
,
int
link
)
{
...
...
@@ -304,382 +289,269 @@ nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
unk
);
}
static
int
nouveau_dp_lane_count_set
(
struct
drm_encoder
*
encoder
,
uint8_t
cmd
)
{
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
uint32_t
tmp
;
int
or
=
nv_encoder
->
or
,
link
=
!
(
nv_encoder
->
dcb
->
sorconf
.
link
&
1
);
tmp
=
nv_rd32
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
));
tmp
&=
~
(
NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED
|
NV50_SOR_DP_CTRL_LANE_MASK
);
tmp
|=
((
1
<<
(
cmd
&
DP_LANE_COUNT_MASK
))
-
1
)
<<
16
;
if
(
cmd
&
DP_LANE_COUNT_ENHANCED_FRAME_EN
)
tmp
|=
NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED
;
nv_wr32
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
),
tmp
);
return
auxch_wr
(
encoder
,
DP_LANE_COUNT_SET
,
&
cmd
,
1
);
}
/******************************************************************************
* link training
*****************************************************************************/
struct
dp_state
{
struct
dcb_entry
*
dcb
;
int
auxch
;
int
crtc
;
int
or
;
int
link
;
int
enh_frame
;
int
link_nr
;
u32
link_bw
;
u8
stat
[
6
];
u8
conf
[
4
];
};
static
int
nouveau_dp_link_bw_set
(
struct
drm_encoder
*
encoder
,
uint8_t
cmd
)
static
void
dp_set_link_config
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
)
{
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
uint32_t
tmp
;
int
reg
=
0x614300
+
(
nv_encoder
->
or
*
0x800
);
int
or
=
dp
->
or
,
link
=
dp
->
link
;
u32
clk_sor
,
dp_ctrl
;
u8
sink
[
2
];
tmp
=
nv_rd32
(
dev
,
reg
);
tmp
&=
0xfff3ffff
;
if
(
cmd
==
DP_LINK_BW_2_7
)
tmp
|=
0x00040000
;
nv_wr32
(
dev
,
reg
,
tmp
);
NV_DEBUG_KMS
(
dev
,
"%d lanes at %d KB/s
\n
"
,
dp
->
link_nr
,
dp
->
link_bw
);
return
auxch_wr
(
encoder
,
DP_LINK_BW_SET
,
&
cmd
,
1
);
}
switch
(
dp
->
link_bw
)
{
case
270000
:
clk_sor
=
0x00040000
;
sink
[
0
]
=
DP_LINK_BW_2_7
;
break
;
default:
clk_sor
=
0x00000000
;
sink
[
0
]
=
DP_LINK_BW_1_62
;
break
;
}
static
int
nouveau_dp_link_train_set
(
struct
drm_encoder
*
encoder
,
int
pattern
)
{
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
uint32_t
tmp
;
uint8_t
cmd
;
int
or
=
nv_encoder
->
or
,
link
=
!
(
nv_encoder
->
dcb
->
sorconf
.
link
&
1
);
int
ret
;
dp_ctrl
=
((
1
<<
dp
->
link_nr
)
-
1
)
<<
16
;
sink
[
1
]
=
dp
->
link_nr
;
if
(
dp
->
enh_frame
)
{
dp_ctrl
|=
0x00004000
;
sink
[
1
]
|=
DP_LANE_COUNT_ENHANCED_FRAME_EN
;
}
tmp
=
nv_rd32
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
));
tmp
&=
~
NV50_SOR_DP_CTRL_TRAINING_PATTERN
;
tmp
|=
(
pattern
<<
24
);
nv_wr32
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
),
tmp
);
nv_mask
(
dev
,
0x614300
+
(
or
*
0x800
),
0x000c0000
,
clk_sor
);
nv_mask
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
),
0x001f4000
,
dp_ctrl
);
ret
=
auxch_rd
(
encoder
,
DP_TRAINING_PATTERN_SET
,
&
cmd
,
1
);
if
(
ret
)
return
ret
;
cmd
&=
~
DP_TRAINING_PATTERN_MASK
;
cmd
|=
(
pattern
&
DP_TRAINING_PATTERN_MASK
);
return
auxch_wr
(
encoder
,
DP_TRAINING_PATTERN_SET
,
&
cmd
,
1
);
auxch_tx
(
dev
,
dp
->
auxch
,
8
,
DP_LINK_BW_SET
,
sink
,
2
);
}
static
int
nouveau_dp_max_voltage_swing
(
struct
drm_encoder
*
encoder
)
static
void
dp_set_training_pattern
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
,
u8
tp
)
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
bit_displayport_encoder_table_entry
*
dpse
;
struct
bit_displayport_encoder_table
*
dpe
;
int
i
,
dpe_headerlen
,
max_vs
=
0
;
dpe
=
nouveau_bios_dp_table
(
dev
,
nv_encoder
->
dcb
,
&
dpe_headerlen
);
if
(
!
dpe
)
return
false
;
dpse
=
(
void
*
)((
char
*
)
dpe
+
dpe_headerlen
);
for
(
i
=
0
;
i
<
dpe_headerlen
;
i
++
,
dpse
++
)
{
if
(
dpse
->
vs_level
>
max_vs
)
max_vs
=
dpse
->
vs_level
;
}
return
max_vs
;
NV_DEBUG_KMS
(
dev
,
"training pattern %d
\n
"
,
tp
);
nv_mask
(
dev
,
NV50_SOR_DP_CTRL
(
dp
->
or
,
dp
->
link
),
0x0f000000
,
tp
<<
24
);
auxch_tx
(
dev
,
dp
->
auxch
,
8
,
DP_TRAINING_PATTERN_SET
,
&
tp
,
1
);
}
static
int
nouveau_dp_max_pre_emphasis
(
struct
drm_encoder
*
encoder
,
int
vs
)
dp_link_train_commit
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
)
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
bit_displayport_encoder_table_entry
*
dpse
;
struct
bit_displayport_encoder_table
*
dpe
;
int
i
,
dpe_headerlen
,
max_pre
=
0
;
u32
mask
=
0
,
drv
=
0
,
pre
=
0
,
unk
=
0
;
u8
shifts
[
4
]
=
{
16
,
8
,
0
,
24
};
u8
*
bios
,
*
last
,
headerlen
;
int
link
=
dp
->
link
;
int
or
=
dp
->
or
;
int
i
;
bios
=
nouveau_bios_dp_table
(
dev
,
dp
->
dcb
,
&
headerlen
);
last
=
bios
+
headerlen
+
(
bios
[
4
]
*
5
);
for
(
i
=
0
;
i
<
dp
->
link_nr
;
i
++
)
{
u8
lane
=
(
dp
->
stat
[
4
+
(
i
>>
1
)]
>>
((
i
&
1
)
*
4
))
&
0xf
;
u8
*
conf
=
bios
+
headerlen
;
while
(
conf
<
last
)
{
if
((
lane
&
3
)
==
conf
[
0
]
&&
(
lane
>>
2
)
==
conf
[
1
])
break
;
conf
+=
5
;
}
dpe
=
nouveau_bios_dp_table
(
dev
,
nv_encoder
->
dcb
,
&
dpe_headerlen
);
if
(
!
dpe
)
return
false
;
dpse
=
(
void
*
)((
char
*
)
dpe
+
dpe_headerlen
);
if
(
conf
==
last
)
return
-
EINVAL
;
for
(
i
=
0
;
i
<
dpe_headerlen
;
i
++
,
dpse
++
)
{
if
(
dpse
->
vs_level
!=
vs
)
continue
;
dp
->
conf
[
i
]
=
(
conf
[
1
]
<<
3
)
|
conf
[
0
];
if
(
conf
[
0
]
==
DP_TRAIN_VOLTAGE_SWING_1200
)
dp
->
conf
[
i
]
|=
DP_TRAIN_MAX_SWING_REACHED
;
if
(
conf
[
1
]
==
DP_TRAIN_PRE_EMPHASIS_9_5
)
dp
->
conf
[
i
]
|=
DP_TRAIN_MAX_PRE_EMPHASIS_REACHED
;
if
(
dpse
->
pre_level
>
max_pre
)
max_pre
=
dpse
->
pre_level
;
NV_DEBUG_KMS
(
dev
,
"config lane %d %02x
\n
"
,
i
,
dp
->
conf
[
i
]);
mask
|=
0xff
<<
shifts
[
i
];
drv
|=
conf
[
2
]
<<
shifts
[
i
];
pre
|=
conf
[
3
]
<<
shifts
[
i
];
unk
=
(
unk
&
~
0x0000ff00
)
|
(
conf
[
4
]
<<
8
);
unk
|=
1
<<
(
shifts
[
i
]
>>
3
);
}
return
max_pre
;
nv_mask
(
dev
,
NV50_SOR_DP_UNK118
(
or
,
link
),
mask
,
drv
);
nv_mask
(
dev
,
NV50_SOR_DP_UNK120
(
or
,
link
),
mask
,
pre
);
nv_mask
(
dev
,
NV50_SOR_DP_UNK130
(
or
,
link
),
0x0000ff0f
,
unk
);
return
auxch_tx
(
dev
,
dp
->
auxch
,
8
,
DP_TRAINING_LANE0_SET
,
dp
->
conf
,
4
);
}
static
bool
nouveau_dp_link_train_adjust
(
struct
drm_encoder
*
encoder
,
uint8_t
*
config
)
static
int
dp_link_train_update
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
,
u32
delay
)
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
bit_displayport_encoder_table
*
dpe
;
int
ret
,
i
,
dpe_headerlen
,
vs
=
0
,
pre
=
0
;
uint8_t
request
[
2
];
int
ret
;
dpe
=
nouveau_bios_dp_table
(
dev
,
nv_encoder
->
dcb
,
&
dpe_headerlen
);
if
(
!
dpe
)
return
false
;
udelay
(
delay
);
ret
=
auxch_
rd
(
encoder
,
DP_ADJUST_REQUEST_LANE0_1
,
request
,
2
);
ret
=
auxch_
tx
(
dev
,
dp
->
auxch
,
9
,
DP_LANE0_1_STATUS
,
dp
->
stat
,
6
);
if
(
ret
)
return
false
;
return
ret
;
NV_DEBUG_KMS
(
dev
,
"
\t\t
adjust 0x%02x 0x%02x
\n
"
,
request
[
0
],
request
[
1
]);
NV_DEBUG_KMS
(
dev
,
"status %02x %02x %02x %02x %02x %02x
\n
"
,
dp
->
stat
[
0
],
dp
->
stat
[
1
],
dp
->
stat
[
2
],
dp
->
stat
[
3
],
dp
->
stat
[
4
],
dp
->
stat
[
5
]);
return
0
;
}
/* Keep all lanes at the same level.. */
for
(
i
=
0
;
i
<
nv_encoder
->
dp
.
link_nr
;
i
++
)
{
int
lane_req
=
(
request
[
i
>>
1
]
>>
((
i
&
1
)
<<
2
))
&
0xf
;
int
lane_vs
=
lane_req
&
3
;
int
lane_pre
=
(
lane_req
>>
2
)
&
3
;
static
int
dp_link_train_cr
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
)
{
bool
cr_done
=
false
,
abort
=
false
;
int
voltage
=
dp
->
conf
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
;
int
tries
=
0
,
i
;
if
(
lane_vs
>
vs
)
vs
=
lane_vs
;
if
(
lane_pre
>
pre
)
pre
=
lane_pre
;
}
dp_set_training_pattern
(
dev
,
dp
,
DP_TRAINING_PATTERN_1
);
if
(
vs
>=
nouveau_dp_max_voltage_swing
(
encoder
))
{
vs
=
nouveau_dp_max_voltage_swing
(
encoder
);
vs
|=
4
;
}
do
{
if
(
dp_link_train_commit
(
dev
,
dp
)
||
dp_link_train_update
(
dev
,
dp
,
100
))
break
;
if
(
pre
>=
nouveau_dp_max_pre_emphasis
(
encoder
,
vs
&
3
))
{
pre
=
nouveau_dp_max_pre_emphasis
(
encoder
,
vs
&
3
);
pre
|=
4
;
}
cr_done
=
true
;
for
(
i
=
0
;
i
<
dp
->
link_nr
;
i
++
)
{
u8
lane
=
(
dp
->
stat
[
i
>>
1
]
>>
((
i
&
1
)
*
4
))
&
0xf
;
if
(
!
(
lane
&
DP_LANE_CR_DONE
))
{
cr_done
=
false
;
if
(
dp
->
conf
[
i
]
&
DP_TRAIN_MAX_SWING_REACHED
)
abort
=
true
;
break
;
}
}
/* Update the configuration for all lanes.. */
for
(
i
=
0
;
i
<
nv_encoder
->
dp
.
link_nr
;
i
++
)
config
[
i
]
=
(
pre
<<
3
)
|
vs
;
if
((
dp
->
conf
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
)
!=
voltage
)
{
voltage
=
dp
->
conf
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
;
tries
=
0
;
}
}
while
(
!
cr_done
&&
!
abort
&&
++
tries
<
5
);
return
true
;
return
cr_done
?
0
:
-
1
;
}
static
bool
nouveau_dp_link_train_commit
(
struct
drm_encoder
*
encoder
,
uint8_t
*
config
)
static
int
dp_link_train_eq
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
)
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
bit_displayport_encoder_table_entry
*
dpse
;
struct
bit_displayport_encoder_table
*
dpe
;
int
or
=
nv_encoder
->
or
,
link
=
!
(
nv_encoder
->
dcb
->
sorconf
.
link
&
1
);
int
dpe_headerlen
,
ret
,
i
;
bool
eq_done
,
cr_done
=
true
;
int
tries
=
0
,
i
;
NV_DEBUG_KMS
(
dev
,
"
\t\t
config 0x%02x 0x%02x 0x%02x 0x%02x
\n
"
,
config
[
0
],
config
[
1
],
config
[
2
],
config
[
3
]);
dp_set_training_pattern
(
dev
,
dp
,
DP_TRAINING_PATTERN_2
);
dpe
=
nouveau_bios_dp_table
(
dev
,
nv_encoder
->
dcb
,
&
dpe_headerlen
);
if
(
!
dpe
)
return
false
;
dpse
=
(
void
*
)((
char
*
)
dpe
+
dpe_headerlen
);
for
(
i
=
0
;
i
<
dpe
->
record_nr
;
i
++
,
dpse
++
)
{
if
(
dpse
->
vs_level
==
(
config
[
0
]
&
3
)
&&
dpse
->
pre_level
==
((
config
[
0
]
>>
3
)
&
3
))
do
{
if
(
dp_link_train_update
(
dev
,
dp
,
400
))
break
;
}
BUG_ON
(
i
==
dpe
->
record_nr
);
for
(
i
=
0
;
i
<
nv_encoder
->
dp
.
link_nr
;
i
++
)
{
const
int
shift
[
4
]
=
{
16
,
8
,
0
,
24
};
uint32_t
mask
=
0xff
<<
shift
[
i
];
uint32_t
reg0
,
reg1
,
reg2
;
reg0
=
nv_rd32
(
dev
,
NV50_SOR_DP_UNK118
(
or
,
link
))
&
~
mask
;
reg0
|=
(
dpse
->
reg0
<<
shift
[
i
]);
reg1
=
nv_rd32
(
dev
,
NV50_SOR_DP_UNK120
(
or
,
link
))
&
~
mask
;
reg1
|=
(
dpse
->
reg1
<<
shift
[
i
]);
reg2
=
nv_rd32
(
dev
,
NV50_SOR_DP_UNK130
(
or
,
link
))
&
0xffff00ff
;
reg2
|=
(
dpse
->
reg2
<<
8
);
nv_wr32
(
dev
,
NV50_SOR_DP_UNK118
(
or
,
link
),
reg0
);
nv_wr32
(
dev
,
NV50_SOR_DP_UNK120
(
or
,
link
),
reg1
);
nv_wr32
(
dev
,
NV50_SOR_DP_UNK130
(
or
,
link
),
reg2
);
}
ret
=
auxch_wr
(
encoder
,
DP_TRAINING_LANE0_SET
,
config
,
4
);
if
(
ret
)
return
false
;
eq_done
=
!!
(
dp
->
stat
[
2
]
&
DP_INTERLANE_ALIGN_DONE
);
for
(
i
=
0
;
i
<
dp
->
link_nr
&&
eq_done
;
i
++
)
{
u8
lane
=
(
dp
->
stat
[
i
>>
1
]
>>
((
i
&
1
)
*
4
))
&
0xf
;
if
(
!
(
lane
&
DP_LANE_CR_DONE
))
cr_done
=
false
;
if
(
!
(
lane
&
DP_LANE_CHANNEL_EQ_DONE
)
||
!
(
lane
&
DP_LANE_SYMBOL_LOCKED
))
eq_done
=
false
;
}
return
true
;
if
(
dp_link_train_commit
(
dev
,
dp
))
break
;
}
while
(
!
eq_done
&&
cr_done
&&
++
tries
<=
5
);
return
eq_done
?
0
:
-
1
;
}
bool
nouveau_dp_link_train
(
struct
drm_encoder
*
encoder
,
u32
datarate
)
{
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
drm_nouveau_private
*
dev_priv
=
dev
->
dev_private
;
struct
drm_nouveau_private
*
dev_priv
=
encoder
->
dev
->
dev_private
;
struct
nouveau_gpio_engine
*
pgpio
=
&
dev_priv
->
engine
.
gpio
;
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
struct
nouveau_connector
*
nv_connector
;
struct
bit_displayport_encoder_table
*
dpe
;
int
dpe_headerlen
;
uint8_t
config
[
4
],
status
[
3
];
bool
cr_done
,
cr_max_vs
,
eq_done
,
hpd_state
;
int
ret
=
0
,
i
,
tries
,
voltage
;
NV_DEBUG_KMS
(
dev
,
"link training!!
\n
"
);
nv_connector
=
nouveau_encoder_connector_get
(
nv_encoder
);
if
(
!
nv_connector
)
return
false
;
dpe
=
nouveau_bios_dp_table
(
dev
,
nv_encoder
->
dcb
,
&
dpe_headerlen
);
if
(
!
dpe
)
{
NV_ERROR
(
dev
,
"SOR-%d: no DP encoder table!
\n
"
,
nv_encoder
->
or
);
return
false
;
}
/* disable hotplug detect, this flips around on some panels during
* link training.
*/
hpd_state
=
pgpio
->
irq_enable
(
dev
,
nv_connector
->
dcb
->
gpio_tag
,
false
);
if
(
dpe
->
script0
)
{
NV_DEBUG_KMS
(
dev
,
"SOR-%d: running DP script 0
\n
"
,
nv_encoder
->
or
);
nouveau_bios_run_init_table
(
dev
,
le16_to_cpu
(
dpe
->
script0
),
nv_encoder
->
dcb
,
-
1
);
}
train:
cr_done
=
eq_done
=
false
;
/* set link configuration */
NV_DEBUG_KMS
(
dev
,
"
\t
begin train: bw %d, lanes %d
\n
"
,
nv_encoder
->
dp
.
link_bw
,
nv_encoder
->
dp
.
link_nr
);
ret
=
nouveau_dp_link_bw_set
(
encoder
,
nv_encoder
->
dp
.
link_bw
);
if
(
ret
)
return
false
;
config
[
0
]
=
nv_encoder
->
dp
.
link_nr
;
if
(
nv_encoder
->
dp
.
dpcd_version
>=
0x11
&&
nv_encoder
->
dp
.
enhanced_frame
)
config
[
0
]
|=
DP_LANE_COUNT_ENHANCED_FRAME_EN
;
struct
nouveau_crtc
*
nv_crtc
=
nouveau_crtc
(
encoder
->
crtc
);
struct
nouveau_connector
*
nv_connector
=
nouveau_encoder_connector_get
(
nv_encoder
);
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
nouveau_i2c_chan
*
auxch
;
const
u32
bw_list
[]
=
{
270000
,
162000
,
0
};
const
u32
*
link_bw
=
bw_list
;
struct
dp_state
dp
;
u8
*
bios
,
headerlen
;
u16
script
;
ret
=
nouveau_dp_lane_count_set
(
encoder
,
config
[
0
]
);
if
(
ret
)
auxch
=
nouveau_i2c_find
(
dev
,
nv_encoder
->
dcb
->
i2c_index
);
if
(
!
auxch
)
return
false
;
/* clock recovery */
NV_DEBUG_KMS
(
dev
,
"
\t
begin cr
\n
"
);
ret
=
nouveau_dp_link_train_set
(
encoder
,
DP_TRAINING_PATTERN_1
);
if
(
ret
)
goto
stop
;
tries
=
0
;
voltage
=
-
1
;
memset
(
config
,
0x00
,
sizeof
(
config
));
for
(;;)
{
if
(
!
nouveau_dp_link_train_commit
(
encoder
,
config
))
break
;
udelay
(
100
);
bios
=
nouveau_bios_dp_table
(
dev
,
nv_encoder
->
dcb
,
&
headerlen
);
if
(
!
bios
)
return
-
EINVAL
;
ret
=
auxch_rd
(
encoder
,
DP_LANE0_1_STATUS
,
status
,
2
);
if
(
ret
)
break
;
NV_DEBUG_KMS
(
dev
,
"
\t\t
status: 0x%02x 0x%02x
\n
"
,
status
[
0
],
status
[
1
]);
cr_done
=
true
;
cr_max_vs
=
false
;
for
(
i
=
0
;
i
<
nv_encoder
->
dp
.
link_nr
;
i
++
)
{
int
lane
=
(
status
[
i
>>
1
]
>>
((
i
&
1
)
*
4
))
&
0xf
;
if
(
!
(
lane
&
DP_LANE_CR_DONE
))
{
cr_done
=
false
;
if
(
config
[
i
]
&
DP_TRAIN_MAX_PRE_EMPHASIS_REACHED
)
cr_max_vs
=
true
;
break
;
}
}
if
((
config
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
)
!=
voltage
)
{
voltage
=
config
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
;
tries
=
0
;
}
if
(
cr_done
||
cr_max_vs
||
(
++
tries
==
5
))
break
;
if
(
!
nouveau_dp_link_train_adjust
(
encoder
,
config
))
break
;
}
if
(
!
cr_done
)
goto
stop
;
/* channel equalisation */
NV_DEBUG_KMS
(
dev
,
"
\t
begin eq
\n
"
);
ret
=
nouveau_dp_link_train_set
(
encoder
,
DP_TRAINING_PATTERN_2
);
if
(
ret
)
goto
stop
;
dp
.
dcb
=
nv_encoder
->
dcb
;
dp
.
crtc
=
nv_crtc
->
index
;
dp
.
auxch
=
auxch
->
rd
;
dp
.
or
=
nv_encoder
->
or
;
dp
.
link
=
!
(
nv_encoder
->
dcb
->
sorconf
.
link
&
1
);
dp
.
enh_frame
=
nv_encoder
->
dp
.
enhanced_frame
;
for
(
tries
=
0
;
tries
<=
5
;
tries
++
)
{
udelay
(
400
);
/* some sinks toggle hotplug in response to some of the actions
* we take during link training (DP_SET_POWER is one), we need
* to ignore them for the moment to avoid races.
*/
pgpio
->
irq_enable
(
dev
,
nv_connector
->
dcb
->
gpio_tag
,
false
);
ret
=
auxch_rd
(
encoder
,
DP_LANE0_1_STATUS
,
status
,
3
);
if
(
ret
)
break
;
NV_DEBUG_KMS
(
dev
,
"
\t\t
status: 0x%02x 0x%02x
\n
"
,
status
[
0
],
status
[
1
]);
/* execute pre-train script from vbios */
nouveau_bios_run_init_table
(
dev
,
ROM16
(
bios
[
6
]),
dp
.
dcb
,
dp
.
crtc
);
eq_done
=
true
;
if
(
!
(
status
[
2
]
&
DP_INTERLANE_ALIGN_DONE
)
)
eq_done
=
false
;
/* start off at highest link rate supported by encoder and display */
if
(
nv_encoder
->
dp
.
link_bw
==
DP_LINK_BW_1_62
)
link_bw
++
;
for
(
i
=
0
;
eq_done
&&
i
<
nv_encoder
->
dp
.
link_nr
;
i
++
)
{
int
lane
=
(
status
[
i
>>
1
]
>>
((
i
&
1
)
*
4
))
&
0xf
;
while
(
link_bw
[
0
])
{
/* find minimum required lane count at this link rate */
dp
.
link_nr
=
nv_encoder
->
dp
.
link_nr
;
while
((
dp
.
link_nr
>>
1
)
*
link_bw
[
0
]
>
datarate
)
dp
.
link_nr
>>=
1
;
if
(
!
(
lane
&
DP_LANE_CR_DONE
))
{
cr_done
=
false
;
break
;
}
/* drop link rate to minimum with this lane count */
while
((
link_bw
[
1
]
*
dp
.
link_nr
)
>
datarate
)
link_bw
++
;
dp
.
link_bw
=
link_bw
[
0
];
if
(
!
(
lane
&
DP_LANE_CHANNEL_EQ_DONE
)
||
!
(
lane
&
DP_LANE_SYMBOL_LOCKED
))
{
eq_done
=
false
;
break
;
}
}
/* program selected link configuration */
dp_set_link_config
(
dev
,
&
dp
);
if
(
eq_done
||
!
cr_done
)
/* attempt to train the link at this configuration */
memset
(
dp
.
stat
,
0x00
,
sizeof
(
dp
.
stat
));
if
(
!
dp_link_train_cr
(
dev
,
&
dp
)
&&
!
dp_link_train_eq
(
dev
,
&
dp
))
break
;
if
(
!
nouveau_dp_link_train_adjust
(
encoder
,
config
)
||
!
nouveau_dp_link_train_commit
(
encoder
,
config
))
break
;
/* retry at lower rate */
link_bw
++
;
}
stop:
/* end link training */
ret
=
nouveau_dp_link_train_set
(
encoder
,
DP_TRAINING_PATTERN_DISABLE
);
if
(
ret
)
return
false
;
/* retry at a lower setting, if possible */
if
(
!
ret
&&
!
(
eq_done
&&
cr_done
))
{
NV_DEBUG_KMS
(
dev
,
"
\t
we failed
\n
"
);
if
(
nv_encoder
->
dp
.
link_bw
!=
DP_LINK_BW_1_62
)
{
NV_DEBUG_KMS
(
dev
,
"retry link training at low rate
\n
"
);
nv_encoder
->
dp
.
link_bw
=
DP_LINK_BW_1_62
;
goto
train
;
}
}
/* finish link training */
dp_set_training_pattern
(
dev
,
&
dp
,
DP_TRAINING_PATTERN_DISABLE
);
if
(
dpe
->
script1
)
{
NV_DEBUG_KMS
(
dev
,
"SOR-%d: running DP script 1
\n
"
,
nv_encoder
->
or
);
nouveau_bios_run_init_table
(
dev
,
le16_to_cpu
(
dpe
->
script1
),
nv_encoder
->
dcb
,
-
1
);
}
/* execute post-train script from vbios */
nouveau_bios_run_init_table
(
dev
,
ROM16
(
bios
[
8
]),
dp
.
dcb
,
dp
.
crtc
);
/* re-enable hotplug detect */
pgpio
->
irq_enable
(
dev
,
nv_connector
->
dcb
->
gpio_tag
,
hpd_state
);
return
eq_done
;
pgpio
->
irq_enable
(
dev
,
nv_connector
->
dcb
->
gpio_tag
,
true
);
return
true
;
}
bool
...
...
drivers/gpu/drm/nouveau/nouveau_drv.h
浏览文件 @
27a45987
...
...
@@ -1081,7 +1081,7 @@ extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
extern
int
nouveau_bios_run_display_table
(
struct
drm_device
*
,
u16
id
,
int
clk
,
struct
dcb_entry
*
,
int
crtc
);
extern
void
*
nouveau_bios_dp_table
(
struct
drm_device
*
,
struct
dcb_entry
*
,
int
*
length
);
u8
*
headerlen
);
extern
bool
nouveau_bios_fp_mode
(
struct
drm_device
*
,
struct
drm_display_mode
*
);
extern
uint8_t
*
nouveau_bios_embedded_edid
(
struct
drm_device
*
);
extern
int
nouveau_bios_parse_lvds_table
(
struct
drm_device
*
,
int
pxclk
,
...
...
drivers/gpu/drm/nouveau/nouveau_encoder.h
浏览文件 @
27a45987
...
...
@@ -84,21 +84,4 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
int
nv50_sor_create
(
struct
drm_connector
*
,
struct
dcb_entry
*
);
int
nv50_dac_create
(
struct
drm_connector
*
,
struct
dcb_entry
*
);
struct
bit_displayport_encoder_table
{
uint32_t
match
;
uint8_t
record_nr
;
uint8_t
unknown
;
uint16_t
script0
;
uint16_t
script1
;
uint16_t
unknown_table
;
}
__attribute__
((
packed
));
struct
bit_displayport_encoder_table_entry
{
uint8_t
vs_level
;
uint8_t
pre_level
;
uint8_t
reg0
;
uint8_t
reg1
;
uint8_t
reg2
;
}
__attribute__
((
packed
));
#endif
/* __NOUVEAU_ENCODER_H__ */
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录