Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
27a45987
Commit
27a45987
authored
Aug 04, 2011
by
Ben Skeggs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
drm/nouveau/dp: restructure link training code
Signed-off-by:
Ben Skeggs
<
bskeggs@redhat.com
>
parent
a002fece
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
206 additions
and
352 deletions
+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
No files found.
drivers/gpu/drm/nouveau/nouveau_bios.c
View file @
27a45987
...
@@ -1179,19 +1179,18 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
...
@@ -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
dcb_entry
*
dcb
=
bios
->
display
.
output
;
struct
drm_device
*
dev
=
bios
->
dev
;
struct
drm_device
*
dev
=
bios
->
dev
;
uint8_t
cond
=
bios
->
data
[
offset
+
1
];
uint8_t
cond
=
bios
->
data
[
offset
+
1
];
int
dummy
;
uint8_t
*
table
,
headerlen
;
BIOSLOG
(
bios
,
"0x%04X: subop 0x%02X
\n
"
,
offset
,
cond
);
BIOSLOG
(
bios
,
"0x%04X: subop 0x%02X
\n
"
,
offset
,
cond
);
if
(
!
iexec
->
execute
)
if
(
!
iexec
->
execute
)
return
3
;
return
3
;
dpe
=
nouveau_bios_dp_table
(
dev
,
dcb
,
&
dummy
);
table
=
nouveau_bios_dp_table
(
dev
,
dcb
,
&
headerlen
);
if
(
!
dp
e
)
{
if
(
!
tabl
e
)
{
NV_ERROR
(
dev
,
"0x%04X: INIT_3A: no encoder table!!
\n
"
,
offset
);
NV_ERROR
(
dev
,
"0x%04X: INIT_3A: no encoder table!!
\n
"
,
offset
);
return
3
;
return
3
;
}
}
...
@@ -1208,7 +1207,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
...
@@ -1208,7 +1207,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
break
;
break
;
case
1
:
case
1
:
case
2
:
case
2
:
if
(
!
(
dpe
->
unknown
&
cond
))
if
(
!
(
table
[
5
]
&
cond
))
iexec
->
execute
=
false
;
iexec
->
execute
=
false
;
break
;
break
;
case
5
:
case
5
:
...
@@ -4480,7 +4479,7 @@ bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
...
@@ -4480,7 +4479,7 @@ bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
void
*
void
*
nouveau_bios_dp_table
(
struct
drm_device
*
dev
,
struct
dcb_entry
*
dcbent
,
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
drm_nouveau_private
*
dev_priv
=
dev
->
dev_private
;
struct
nvbios
*
bios
=
&
dev_priv
->
vbios
;
struct
nvbios
*
bios
=
&
dev_priv
->
vbios
;
...
@@ -4498,7 +4497,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
...
@@ -4498,7 +4497,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
return
NULL
;
return
NULL
;
}
}
*
length
=
table
[
4
];
*
headerlen
=
table
[
4
];
return
bios_output_config_match
(
dev
,
dcbent
,
return
bios_output_config_match
(
dev
,
dcbent
,
bios
->
display
.
dp_table_ptr
+
table
[
1
],
bios
->
display
.
dp_table_ptr
+
table
[
1
],
table
[
2
],
table
[
3
],
table
[
0
]
>=
0x21
);
table
[
2
],
table
[
3
],
table
[
0
]
>=
0x21
);
...
...
drivers/gpu/drm/nouveau/nouveau_dp.c
View file @
27a45987
...
@@ -28,6 +28,7 @@
...
@@ -28,6 +28,7 @@
#include "nouveau_i2c.h"
#include "nouveau_i2c.h"
#include "nouveau_connector.h"
#include "nouveau_connector.h"
#include "nouveau_encoder.h"
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
/******************************************************************************
/******************************************************************************
* aux channel util functions
* aux channel util functions
...
@@ -178,22 +179,6 @@ auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
...
@@ -178,22 +179,6 @@ auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
return
0
;
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
static
u32
dp_link_bw_get
(
struct
drm_device
*
dev
,
int
or
,
int
link
)
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)
...
@@ -304,382 +289,269 @@ nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
unk
);
unk
);
}
}
static
int
/******************************************************************************
nouveau_dp_lane_count_set
(
struct
drm_encoder
*
encoder
,
uint8_t
cmd
)
* link training
{
*****************************************************************************/
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
dp_state
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
struct
dcb_entry
*
dcb
;
uint32_t
tmp
;
int
auxch
;
int
or
=
nv_encoder
->
or
,
link
=
!
(
nv_encoder
->
dcb
->
sorconf
.
link
&
1
);
int
crtc
;
int
or
;
tmp
=
nv_rd32
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
));
int
link
;
tmp
&=
~
(
NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED
|
int
enh_frame
;
NV50_SOR_DP_CTRL_LANE_MASK
);
int
link_nr
;
tmp
|=
((
1
<<
(
cmd
&
DP_LANE_COUNT_MASK
))
-
1
)
<<
16
;
u32
link_bw
;
if
(
cmd
&
DP_LANE_COUNT_ENHANCED_FRAME_EN
)
u8
stat
[
6
];
tmp
|=
NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED
;
u8
conf
[
4
];
nv_wr32
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
),
tmp
);
};
return
auxch_wr
(
encoder
,
DP_LANE_COUNT_SET
,
&
cmd
,
1
);
}
static
int
static
void
nouveau_dp_link_bw_set
(
struct
drm_encoder
*
encoder
,
uint8_t
cmd
)
dp_set_link_config
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
)
{
{
struct
drm_device
*
dev
=
encoder
->
dev
;
int
or
=
dp
->
or
,
link
=
dp
->
link
;
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
u32
clk_sor
,
dp_ctrl
;
uint32_t
tmp
;
u8
sink
[
2
];
int
reg
=
0x614300
+
(
nv_encoder
->
or
*
0x800
);
tmp
=
nv_rd32
(
dev
,
reg
);
NV_DEBUG_KMS
(
dev
,
"%d lanes at %d KB/s
\n
"
,
dp
->
link_nr
,
dp
->
link_bw
);
tmp
&=
0xfff3ffff
;
if
(
cmd
==
DP_LINK_BW_2_7
)
tmp
|=
0x00040000
;
nv_wr32
(
dev
,
reg
,
tmp
);
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
dp_ctrl
=
((
1
<<
dp
->
link_nr
)
-
1
)
<<
16
;
nouveau_dp_link_train_set
(
struct
drm_encoder
*
encoder
,
int
pattern
)
sink
[
1
]
=
dp
->
link_nr
;
{
if
(
dp
->
enh_frame
)
{
struct
drm_device
*
dev
=
encoder
->
dev
;
dp_ctrl
|=
0x00004000
;
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
sink
[
1
]
|=
DP_LANE_COUNT_ENHANCED_FRAME_EN
;
uint32_t
tmp
;
}
uint8_t
cmd
;
int
or
=
nv_encoder
->
or
,
link
=
!
(
nv_encoder
->
dcb
->
sorconf
.
link
&
1
);
int
ret
;
tmp
=
nv_rd32
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
));
nv_mask
(
dev
,
0x614300
+
(
or
*
0x800
),
0x000c0000
,
clk_sor
);
tmp
&=
~
NV50_SOR_DP_CTRL_TRAINING_PATTERN
;
nv_mask
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
),
0x001f4000
,
dp_ctrl
);
tmp
|=
(
pattern
<<
24
);
nv_wr32
(
dev
,
NV50_SOR_DP_CTRL
(
or
,
link
),
tmp
);
ret
=
auxch_rd
(
encoder
,
DP_TRAINING_PATTERN_SET
,
&
cmd
,
1
);
auxch_tx
(
dev
,
dp
->
auxch
,
8
,
DP_LINK_BW_SET
,
sink
,
2
);
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
);
}
}
static
int
static
void
nouveau_dp_max_voltage_swing
(
struct
drm_encoder
*
encoder
)
dp_set_training_pattern
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
,
u8
tp
)
{
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
NV_DEBUG_KMS
(
dev
,
"training pattern %d
\n
"
,
tp
);
struct
drm_device
*
dev
=
encoder
->
dev
;
nv_mask
(
dev
,
NV50_SOR_DP_CTRL
(
dp
->
or
,
dp
->
link
),
0x0f000000
,
tp
<<
24
);
struct
bit_displayport_encoder_table_entry
*
dpse
;
auxch_tx
(
dev
,
dp
->
auxch
,
8
,
DP_TRAINING_PATTERN_SET
,
&
tp
,
1
);
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
;
}
}
static
int
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
);
u32
mask
=
0
,
drv
=
0
,
pre
=
0
,
unk
=
0
;
struct
drm_device
*
dev
=
encoder
->
dev
;
u8
shifts
[
4
]
=
{
16
,
8
,
0
,
24
};
struct
bit_displayport_encoder_table_entry
*
dpse
;
u8
*
bios
,
*
last
,
headerlen
;
struct
bit_displayport_encoder_table
*
dpe
;
int
link
=
dp
->
link
;
int
i
,
dpe_headerlen
,
max_pre
=
0
;
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
(
conf
==
last
)
if
(
!
dpe
)
return
-
EINVAL
;
return
false
;
dpse
=
(
void
*
)((
char
*
)
dpe
+
dpe_headerlen
);
for
(
i
=
0
;
i
<
dpe_headerlen
;
i
++
,
dpse
++
)
{
dp
->
conf
[
i
]
=
(
conf
[
1
]
<<
3
)
|
conf
[
0
];
if
(
dpse
->
vs_level
!=
vs
)
if
(
conf
[
0
]
==
DP_TRAIN_VOLTAGE_SWING_1200
)
continue
;
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
)
NV_DEBUG_KMS
(
dev
,
"config lane %d %02x
\n
"
,
i
,
dp
->
conf
[
i
]);
max_pre
=
dpse
->
pre_level
;
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
static
int
nouveau_dp_link_train_adjust
(
struct
drm_encoder
*
encoder
,
uint8_t
*
config
)
dp_link_train_update
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
,
u32
delay
)
{
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
int
ret
;
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
];
dpe
=
nouveau_bios_dp_table
(
dev
,
nv_encoder
->
dcb
,
&
dpe_headerlen
);
udelay
(
delay
);
if
(
!
dpe
)
return
false
;
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
)
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.. */
static
int
for
(
i
=
0
;
i
<
nv_encoder
->
dp
.
link_nr
;
i
++
)
{
dp_link_train_cr
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
)
int
lane_req
=
(
request
[
i
>>
1
]
>>
((
i
&
1
)
<<
2
))
&
0xf
;
{
int
lane_vs
=
lane_req
&
3
;
bool
cr_done
=
false
,
abort
=
false
;
int
lane_pre
=
(
lane_req
>>
2
)
&
3
;
int
voltage
=
dp
->
conf
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
;
int
tries
=
0
,
i
;
if
(
lane_vs
>
vs
)
dp_set_training_pattern
(
dev
,
dp
,
DP_TRAINING_PATTERN_1
);
vs
=
lane_vs
;
if
(
lane_pre
>
pre
)
pre
=
lane_pre
;
}
if
(
vs
>=
nouveau_dp_max_voltage_swing
(
encoder
))
{
do
{
vs
=
nouveau_dp_max_voltage_swing
(
encoder
);
if
(
dp_link_train_commit
(
dev
,
dp
)
||
vs
|=
4
;
dp_link_train_update
(
dev
,
dp
,
100
))
}
break
;
if
(
pre
>=
nouveau_dp_max_pre_emphasis
(
encoder
,
vs
&
3
))
{
cr_done
=
true
;
pre
=
nouveau_dp_max_pre_emphasis
(
encoder
,
vs
&
3
);
for
(
i
=
0
;
i
<
dp
->
link_nr
;
i
++
)
{
pre
|=
4
;
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.. */
if
((
dp
->
conf
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
)
!=
voltage
)
{
for
(
i
=
0
;
i
<
nv_encoder
->
dp
.
link_nr
;
i
++
)
voltage
=
dp
->
conf
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
;
config
[
i
]
=
(
pre
<<
3
)
|
vs
;
tries
=
0
;
}
}
while
(
!
cr_done
&&
!
abort
&&
++
tries
<
5
);
return
true
;
return
cr_done
?
0
:
-
1
;
}
}
static
bool
static
int
nouveau_dp_link_train_commit
(
struct
drm_encoder
*
encoder
,
uint8_t
*
config
)
dp_link_train_eq
(
struct
drm_device
*
dev
,
struct
dp_state
*
dp
)
{
{
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
bool
eq_done
,
cr_done
=
true
;
struct
drm_device
*
dev
=
encoder
->
dev
;
int
tries
=
0
,
i
;
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
;
NV_DEBUG_KMS
(
dev
,
"
\t\t
config 0x%02x 0x%02x 0x%02x 0x%02x
\n
"
,
dp_set_training_pattern
(
dev
,
dp
,
DP_TRAINING_PATTERN_2
);
config
[
0
],
config
[
1
],
config
[
2
],
config
[
3
]);
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
++
)
{
do
{
if
(
dpse
->
vs_level
==
(
config
[
0
]
&
3
)
&&
if
(
dp_link_train_update
(
dev
,
dp
,
400
))
dpse
->
pre_level
==
((
config
[
0
]
>>
3
)
&
3
))
break
;
break
;
}
BUG_ON
(
i
==
dpe
->
record_nr
);
eq_done
=
!!
(
dp
->
stat
[
2
]
&
DP_INTERLANE_ALIGN_DONE
);
for
(
i
=
0
;
i
<
dp
->
link_nr
&&
eq_done
;
i
++
)
{
for
(
i
=
0
;
i
<
nv_encoder
->
dp
.
link_nr
;
i
++
)
{
u8
lane
=
(
dp
->
stat
[
i
>>
1
]
>>
((
i
&
1
)
*
4
))
&
0xf
;
const
int
shift
[
4
]
=
{
16
,
8
,
0
,
24
};
if
(
!
(
lane
&
DP_LANE_CR_DONE
))
uint32_t
mask
=
0xff
<<
shift
[
i
];
cr_done
=
false
;
uint32_t
reg0
,
reg1
,
reg2
;
if
(
!
(
lane
&
DP_LANE_CHANNEL_EQ_DONE
)
||
!
(
lane
&
DP_LANE_SYMBOL_LOCKED
))
reg0
=
nv_rd32
(
dev
,
NV50_SOR_DP_UNK118
(
or
,
link
))
&
~
mask
;
eq_done
=
false
;
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
(
dp_link_train_commit
(
dev
,
dp
))
if
(
ret
)
break
;
return
false
;
}
while
(
!
eq_done
&&
cr_done
&&
++
tries
<=
5
)
;
return
true
;
return
eq_done
?
0
:
-
1
;
}
}
bool
bool
nouveau_dp_link_train
(
struct
drm_encoder
*
encoder
,
u32
datarate
)
nouveau_dp_link_train
(
struct
drm_encoder
*
encoder
,
u32
datarate
)
{
{
struct
drm_device
*
dev
=
encoder
->
dev
;
struct
drm_nouveau_private
*
dev_priv
=
encoder
->
dev
->
dev_private
;
struct
drm_nouveau_private
*
dev_priv
=
dev
->
dev_private
;
struct
nouveau_gpio_engine
*
pgpio
=
&
dev_priv
->
engine
.
gpio
;
struct
nouveau_gpio_engine
*
pgpio
=
&
dev_priv
->
engine
.
gpio
;
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
struct
nouveau_encoder
*
nv_encoder
=
nouveau_encoder
(
encoder
);
struct
nouveau_connector
*
nv_connector
;
struct
nouveau_crtc
*
nv_crtc
=
nouveau_crtc
(
encoder
->
crtc
);
struct
bit_displayport_encoder_table
*
dpe
;
struct
nouveau_connector
*
nv_connector
=
int
dpe_headerlen
;
nouveau_encoder_connector_get
(
nv_encoder
);
uint8_t
config
[
4
],
status
[
3
];
struct
drm_device
*
dev
=
encoder
->
dev
;
bool
cr_done
,
cr_max_vs
,
eq_done
,
hpd_state
;
struct
nouveau_i2c_chan
*
auxch
;
int
ret
=
0
,
i
,
tries
,
voltage
;
const
u32
bw_list
[]
=
{
270000
,
162000
,
0
};
const
u32
*
link_bw
=
bw_list
;
NV_DEBUG_KMS
(
dev
,
"link training!!
\n
"
);
struct
dp_state
dp
;
u8
*
bios
,
headerlen
;
nv_connector
=
nouveau_encoder_connector_get
(
nv_encoder
);
u16
script
;
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
;
ret
=
nouveau_dp_lane_count_set
(
encoder
,
config
[
0
]
);
auxch
=
nouveau_i2c_find
(
dev
,
nv_encoder
->
dcb
->
i2c_index
);
if
(
ret
)
if
(
!
auxch
)
return
false
;
return
false
;
/* clock recovery */
bios
=
nouveau_bios_dp_table
(
dev
,
nv_encoder
->
dcb
,
&
headerlen
);
NV_DEBUG_KMS
(
dev
,
"
\t
begin cr
\n
"
);
if
(
!
bios
)
ret
=
nouveau_dp_link_train_set
(
encoder
,
DP_TRAINING_PATTERN_1
);
return
-
EINVAL
;
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
);
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
))
{
dp
.
dcb
=
nv_encoder
->
dcb
;
cr_done
=
false
;
dp
.
crtc
=
nv_crtc
->
index
;
if
(
config
[
i
]
&
DP_TRAIN_MAX_PRE_EMPHASIS_REACHED
)
dp
.
auxch
=
auxch
->
rd
;
cr_max_vs
=
true
;
dp
.
or
=
nv_encoder
->
or
;
break
;
dp
.
link
=
!
(
nv_encoder
->
dcb
->
sorconf
.
link
&
1
);
}
dp
.
enh_frame
=
nv_encoder
->
dp
.
enhanced_frame
;
}
if
((
config
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
)
!=
voltage
)
{
/* some sinks toggle hotplug in response to some of the actions
voltage
=
config
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
;
* we take during link training (DP_SET_POWER is one), we need
tries
=
0
;
* to ignore them for the moment to avoid races.
}
*/
pgpio
->
irq_enable
(
dev
,
nv_connector
->
dcb
->
gpio_tag
,
false
);
if
(
cr_done
||
cr_max_vs
||
(
++
tries
==
5
))
/* execute pre-train script from vbios */
break
;
nouveau_bios_run_init_table
(
dev
,
ROM16
(
bios
[
6
]),
dp
.
dcb
,
dp
.
crtc
)
;
if
(
!
nouveau_dp_link_train_adjust
(
encoder
,
config
))
/* start off at highest link rate supported by encoder and display */
break
;
if
(
nv_encoder
->
dp
.
link_bw
==
DP_LINK_BW_1_62
)
}
link_bw
++
;
if
(
!
cr_done
)
while
(
link_bw
[
0
])
{
goto
stop
;
/* 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
;
/* channel equalisation */
/* drop link rate to minimum with this lane count */
NV_DEBUG_KMS
(
dev
,
"
\t
begin eq
\n
"
);
while
((
link_bw
[
1
]
*
dp
.
link_nr
)
>
datarate
)
ret
=
nouveau_dp_link_train_set
(
encoder
,
DP_TRAINING_PATTERN_2
);
link_bw
++
;
if
(
ret
)
dp
.
link_bw
=
link_bw
[
0
];
goto
stop
;
for
(
tries
=
0
;
tries
<=
5
;
tries
++
)
{
/* program selected link configuration */
udelay
(
400
);
dp_set_link_config
(
dev
,
&
dp
);
ret
=
auxch_rd
(
encoder
,
DP_LANE0_1_STATUS
,
status
,
3
);
/* attempt to train the link at this configuration */
if
(
ret
)
memset
(
dp
.
stat
,
0x00
,
sizeof
(
dp
.
stat
));
if
(
!
dp_link_train_cr
(
dev
,
&
dp
)
&&
!
dp_link_train_eq
(
dev
,
&
dp
))
break
;
break
;
NV_DEBUG_KMS
(
dev
,
"
\t\t
status: 0x%02x 0x%02x
\n
"
,
status
[
0
],
status
[
1
]);
eq_done
=
true
;
if
(
!
(
status
[
2
]
&
DP_INTERLANE_ALIGN_DONE
))
eq_done
=
false
;
for
(
i
=
0
;
eq_done
&&
i
<
nv_encoder
->
dp
.
link_nr
;
i
++
)
{
int
lane
=
(
status
[
i
>>
1
]
>>
((
i
&
1
)
*
4
))
&
0xf
;
if
(
!
(
lane
&
DP_LANE_CR_DONE
))
{
/* retry at lower rate */
cr_done
=
false
;
link_bw
++
;
break
;
}
}
if
(
!
(
lane
&
DP_LANE_CHANNEL_EQ_DONE
)
||
/* finish link training */
!
(
lane
&
DP_LANE_SYMBOL_LOCKED
))
{
dp_set_training_pattern
(
dev
,
&
dp
,
DP_TRAINING_PATTERN_DISABLE
);
eq_done
=
false
;
break
;
}
}
if
(
eq_done
||
!
cr_done
)
break
;
if
(
!
nouveau_dp_link_train_adjust
(
encoder
,
config
)
||
!
nouveau_dp_link_train_commit
(
encoder
,
config
))
break
;
}
stop:
/* execute post-train script from vbios */
/* end link training */
nouveau_bios_run_init_table
(
dev
,
ROM16
(
bios
[
8
]),
dp
.
dcb
,
dp
.
crtc
);
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
;
}
}
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
);
}
/* re-enable hotplug detect */
/* re-enable hotplug detect */
pgpio
->
irq_enable
(
dev
,
nv_connector
->
dcb
->
gpio_tag
,
hpd_state
);
pgpio
->
irq_enable
(
dev
,
nv_connector
->
dcb
->
gpio_tag
,
true
);
return
true
;
return
eq_done
;
}
}
bool
bool
...
...
drivers/gpu/drm/nouveau/nouveau_drv.h
View file @
27a45987
...
@@ -1081,7 +1081,7 @@ extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
...
@@ -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
,
extern
int
nouveau_bios_run_display_table
(
struct
drm_device
*
,
u16
id
,
int
clk
,
struct
dcb_entry
*
,
int
crtc
);
struct
dcb_entry
*
,
int
crtc
);
extern
void
*
nouveau_bios_dp_table
(
struct
drm_device
*
,
struct
dcb_entry
*
,
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
bool
nouveau_bios_fp_mode
(
struct
drm_device
*
,
struct
drm_display_mode
*
);
extern
uint8_t
*
nouveau_bios_embedded_edid
(
struct
drm_device
*
);
extern
uint8_t
*
nouveau_bios_embedded_edid
(
struct
drm_device
*
);
extern
int
nouveau_bios_parse_lvds_table
(
struct
drm_device
*
,
int
pxclk
,
extern
int
nouveau_bios_parse_lvds_table
(
struct
drm_device
*
,
int
pxclk
,
...
...
drivers/gpu/drm/nouveau/nouveau_encoder.h
View file @
27a45987
...
@@ -84,21 +84,4 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
...
@@ -84,21 +84,4 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
int
nv50_sor_create
(
struct
drm_connector
*
,
struct
dcb_entry
*
);
int
nv50_sor_create
(
struct
drm_connector
*
,
struct
dcb_entry
*
);
int
nv50_dac_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__ */
#endif
/* __NOUVEAU_ENCODER_H__ */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment