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
f479c0ba
Commit
f479c0ba
authored
Nov 04, 2016
by
Ben Skeggs
1
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
drm/nouveau/kms/nv50: initial support for DP 1.2 multi-stream
Signed-off-by:
Ben Skeggs
<
bskeggs@redhat.com
>
parent
d665c7e9
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
571 additions
and
2 deletions
+571
-2
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_connector.c
+9
-0
drivers/gpu/drm/nouveau/nouveau_dp.c
drivers/gpu/drm/nouveau/nouveau_dp.c
+7
-1
drivers/gpu/drm/nouveau/nouveau_encoder.h
drivers/gpu/drm/nouveau/nouveau_encoder.h
+2
-0
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_display.c
+553
-1
No files found.
drivers/gpu/drm/nouveau/nouveau_connector.c
View file @
f479c0ba
...
@@ -1118,12 +1118,21 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
...
@@ -1118,12 +1118,21 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
struct
nouveau_drm
*
drm
=
nouveau_drm
(
connector
->
dev
);
struct
nouveau_drm
*
drm
=
nouveau_drm
(
connector
->
dev
);
const
struct
nvif_notify_conn_rep_v0
*
rep
=
notify
->
data
;
const
struct
nvif_notify_conn_rep_v0
*
rep
=
notify
->
data
;
const
char
*
name
=
connector
->
name
;
const
char
*
name
=
connector
->
name
;
struct
nouveau_encoder
*
nv_encoder
;
if
(
rep
->
mask
&
NVIF_NOTIFY_CONN_V0_IRQ
)
{
if
(
rep
->
mask
&
NVIF_NOTIFY_CONN_V0_IRQ
)
{
NV_DEBUG
(
drm
,
"service %s
\n
"
,
name
);
if
((
nv_encoder
=
find_encoder
(
connector
,
DCB_OUTPUT_DP
)))
nv50_mstm_service
(
nv_encoder
->
dp
.
mstm
);
}
else
{
}
else
{
bool
plugged
=
(
rep
->
mask
!=
NVIF_NOTIFY_CONN_V0_UNPLUG
);
bool
plugged
=
(
rep
->
mask
!=
NVIF_NOTIFY_CONN_V0_UNPLUG
);
NV_DEBUG
(
drm
,
"%splugged %s
\n
"
,
plugged
?
""
:
"un"
,
name
);
NV_DEBUG
(
drm
,
"%splugged %s
\n
"
,
plugged
?
""
:
"un"
,
name
);
if
((
nv_encoder
=
find_encoder
(
connector
,
DCB_OUTPUT_DP
)))
{
if
(
!
plugged
)
nv50_mstm_remove
(
nv_encoder
->
dp
.
mstm
);
}
drm_helper_hpd_irq_event
(
connector
->
dev
);
drm_helper_hpd_irq_event
(
connector
->
dev
);
}
}
...
...
drivers/gpu/drm/nouveau/nouveau_dp.c
View file @
f479c0ba
...
@@ -33,6 +33,10 @@
...
@@ -33,6 +33,10 @@
#include <nvif/class.h>
#include <nvif/class.h>
#include <nvif/cl5070.h>
#include <nvif/cl5070.h>
MODULE_PARM_DESC
(
mst
,
"Enable DisplayPort multi-stream (default: enabled)"
);
static
int
nouveau_mst
=
1
;
module_param_named
(
mst
,
nouveau_mst
,
int
,
0400
);
static
void
static
void
nouveau_dp_probe_oui
(
struct
drm_device
*
dev
,
struct
nvkm_i2c_aux
*
aux
,
u8
*
dpcd
)
nouveau_dp_probe_oui
(
struct
drm_device
*
dev
,
struct
nvkm_i2c_aux
*
aux
,
u8
*
dpcd
)
{
{
...
@@ -88,7 +92,9 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder)
...
@@ -88,7 +92,9 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder)
nouveau_dp_probe_oui
(
dev
,
aux
,
dpcd
);
nouveau_dp_probe_oui
(
dev
,
aux
,
dpcd
);
ret
=
nv50_mstm_detect
(
nv_encoder
->
dp
.
mstm
,
dpcd
,
0
);
ret
=
nv50_mstm_detect
(
nv_encoder
->
dp
.
mstm
,
dpcd
,
nouveau_mst
);
if
(
ret
==
1
)
return
NOUVEAU_DP_MST
;
if
(
ret
==
0
)
if
(
ret
==
0
)
return
NOUVEAU_DP_SST
;
return
NOUVEAU_DP_SST
;
return
ret
;
return
ret
;
...
...
drivers/gpu/drm/nouveau/nouveau_encoder.h
View file @
f479c0ba
...
@@ -103,4 +103,6 @@ struct nouveau_connector *
...
@@ -103,4 +103,6 @@ struct nouveau_connector *
nouveau_encoder_connector_get
(
struct
nouveau_encoder
*
encoder
);
nouveau_encoder_connector_get
(
struct
nouveau_encoder
*
encoder
);
int
nv50_mstm_detect
(
struct
nv50_mstm
*
,
u8
dpcd
[
8
],
int
allow
);
int
nv50_mstm_detect
(
struct
nv50_mstm
*
,
u8
dpcd
[
8
],
int
allow
);
void
nv50_mstm_remove
(
struct
nv50_mstm
*
);
void
nv50_mstm_service
(
struct
nv50_mstm
*
);
#endif
/* __NOUVEAU_ENCODER_H__ */
#endif
/* __NOUVEAU_ENCODER_H__ */
drivers/gpu/drm/nouveau/nv50_display.c
View file @
f479c0ba
...
@@ -2804,12 +2804,502 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
...
@@ -2804,12 +2804,502 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
/******************************************************************************
/******************************************************************************
* MST
* MST
*****************************************************************************/
*****************************************************************************/
#define nv50_mstm(p) container_of((p), struct nv50_mstm, mgr)
#define nv50_mstc(p) container_of((p), struct nv50_mstc, connector)
#define nv50_msto(p) container_of((p), struct nv50_msto, encoder)
struct
nv50_mstm
{
struct
nv50_mstm
{
struct
nouveau_encoder
*
outp
;
struct
nouveau_encoder
*
outp
;
struct
drm_dp_mst_topology_mgr
mgr
;
struct
drm_dp_mst_topology_mgr
mgr
;
struct
nv50_msto
*
msto
[
4
];
bool
modified
;
};
struct
nv50_mstc
{
struct
nv50_mstm
*
mstm
;
struct
drm_dp_mst_port
*
port
;
struct
drm_connector
connector
;
struct
drm_display_mode
*
native
;
struct
edid
*
edid
;
int
pbn
;
};
};
struct
nv50_msto
{
struct
drm_encoder
encoder
;
struct
nv50_head
*
head
;
struct
nv50_mstc
*
mstc
;
bool
disabled
;
};
static
struct
drm_dp_payload
*
nv50_msto_payload
(
struct
nv50_msto
*
msto
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
msto
->
encoder
.
dev
);
struct
nv50_mstc
*
mstc
=
msto
->
mstc
;
struct
nv50_mstm
*
mstm
=
mstc
->
mstm
;
int
vcpi
=
mstc
->
port
->
vcpi
.
vcpi
,
i
;
NV_ATOMIC
(
drm
,
"%s: vcpi %d
\n
"
,
msto
->
encoder
.
name
,
vcpi
);
for
(
i
=
0
;
i
<
mstm
->
mgr
.
max_payloads
;
i
++
)
{
struct
drm_dp_payload
*
payload
=
&
mstm
->
mgr
.
payloads
[
i
];
NV_ATOMIC
(
drm
,
"%s: %d: vcpi %d start 0x%02x slots 0x%02x
\n
"
,
mstm
->
outp
->
base
.
base
.
name
,
i
,
payload
->
vcpi
,
payload
->
start_slot
,
payload
->
num_slots
);
}
for
(
i
=
0
;
i
<
mstm
->
mgr
.
max_payloads
;
i
++
)
{
struct
drm_dp_payload
*
payload
=
&
mstm
->
mgr
.
payloads
[
i
];
if
(
payload
->
vcpi
==
vcpi
)
return
payload
;
}
return
NULL
;
}
static
void
nv50_msto_cleanup
(
struct
nv50_msto
*
msto
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
msto
->
encoder
.
dev
);
struct
nv50_mstc
*
mstc
=
msto
->
mstc
;
struct
nv50_mstm
*
mstm
=
mstc
->
mstm
;
NV_ATOMIC
(
drm
,
"%s: msto cleanup
\n
"
,
msto
->
encoder
.
name
);
if
(
mstc
->
port
&&
mstc
->
port
->
vcpi
.
vcpi
>
0
&&
!
nv50_msto_payload
(
msto
))
drm_dp_mst_deallocate_vcpi
(
&
mstm
->
mgr
,
mstc
->
port
);
if
(
msto
->
disabled
)
{
msto
->
mstc
=
NULL
;
msto
->
head
=
NULL
;
msto
->
disabled
=
false
;
}
}
static
void
nv50_msto_prepare
(
struct
nv50_msto
*
msto
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
msto
->
encoder
.
dev
);
struct
nv50_mstc
*
mstc
=
msto
->
mstc
;
struct
nv50_mstm
*
mstm
=
mstc
->
mstm
;
struct
{
struct
nv50_disp_mthd_v1
base
;
struct
nv50_disp_sor_dp_mst_vcpi_v0
vcpi
;
}
args
=
{
.
base
.
version
=
1
,
.
base
.
method
=
NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI
,
.
base
.
hasht
=
mstm
->
outp
->
dcb
->
hasht
,
.
base
.
hashm
=
(
0xf0ff
&
mstm
->
outp
->
dcb
->
hashm
)
|
(
0x0100
<<
msto
->
head
->
base
.
index
),
};
NV_ATOMIC
(
drm
,
"%s: msto prepare
\n
"
,
msto
->
encoder
.
name
);
if
(
mstc
->
port
&&
mstc
->
port
->
vcpi
.
vcpi
>
0
)
{
struct
drm_dp_payload
*
payload
=
nv50_msto_payload
(
msto
);
if
(
payload
)
{
args
.
vcpi
.
start_slot
=
payload
->
start_slot
;
args
.
vcpi
.
num_slots
=
payload
->
num_slots
;
args
.
vcpi
.
pbn
=
mstc
->
port
->
vcpi
.
pbn
;
args
.
vcpi
.
aligned_pbn
=
mstc
->
port
->
vcpi
.
aligned_pbn
;
}
}
NV_ATOMIC
(
drm
,
"%s: %s: %02x %02x %04x %04x
\n
"
,
msto
->
encoder
.
name
,
msto
->
head
->
base
.
base
.
name
,
args
.
vcpi
.
start_slot
,
args
.
vcpi
.
num_slots
,
args
.
vcpi
.
pbn
,
args
.
vcpi
.
aligned_pbn
);
nvif_mthd
(
&
drm
->
display
->
disp
,
0
,
&
args
,
sizeof
(
args
));
}
static
int
nv50_msto_atomic_check
(
struct
drm_encoder
*
encoder
,
struct
drm_crtc_state
*
crtc_state
,
struct
drm_connector_state
*
conn_state
)
{
struct
nv50_mstc
*
mstc
=
nv50_mstc
(
conn_state
->
connector
);
struct
nv50_mstm
*
mstm
=
mstc
->
mstm
;
int
bpp
=
conn_state
->
connector
->
display_info
.
bpc
*
3
;
int
slots
;
mstc
->
pbn
=
drm_dp_calc_pbn_mode
(
crtc_state
->
adjusted_mode
.
clock
,
bpp
);
slots
=
drm_dp_find_vcpi_slots
(
&
mstm
->
mgr
,
mstc
->
pbn
);
if
(
slots
<
0
)
return
slots
;
return
nv50_outp_atomic_check_view
(
encoder
,
crtc_state
,
conn_state
,
mstc
->
native
);
}
static
void
nv50_msto_enable
(
struct
drm_encoder
*
encoder
)
{
struct
nv50_head
*
head
=
nv50_head
(
encoder
->
crtc
);
struct
nv50_msto
*
msto
=
nv50_msto
(
encoder
);
struct
nv50_mstc
*
mstc
=
NULL
;
struct
nv50_mstm
*
mstm
=
NULL
;
struct
drm_connector
*
connector
;
u8
proto
,
depth
;
int
slots
;
bool
r
;
drm_for_each_connector
(
connector
,
encoder
->
dev
)
{
if
(
connector
->
state
->
best_encoder
==
&
msto
->
encoder
)
{
mstc
=
nv50_mstc
(
connector
);
mstm
=
mstc
->
mstm
;
break
;
}
}
if
(
WARN_ON
(
!
mstc
))
return
;
r
=
drm_dp_mst_allocate_vcpi
(
&
mstm
->
mgr
,
mstc
->
port
,
mstc
->
pbn
,
&
slots
);
WARN_ON
(
!
r
);
if
(
mstm
->
outp
->
dcb
->
sorconf
.
link
&
1
)
proto
=
0x8
;
else
proto
=
0x9
;
switch
(
mstc
->
connector
.
display_info
.
bpc
)
{
case
6
:
depth
=
0x2
;
break
;
case
8
:
depth
=
0x5
;
break
;
case
10
:
default:
depth
=
0x6
;
break
;
}
mstm
->
outp
->
update
(
mstm
->
outp
,
head
->
base
.
index
,
&
head
->
base
.
base
.
state
->
adjusted_mode
,
proto
,
depth
);
msto
->
head
=
head
;
msto
->
mstc
=
mstc
;
mstm
->
modified
=
true
;
}
static
void
nv50_msto_disable
(
struct
drm_encoder
*
encoder
)
{
struct
nv50_msto
*
msto
=
nv50_msto
(
encoder
);
struct
nv50_mstc
*
mstc
=
msto
->
mstc
;
struct
nv50_mstm
*
mstm
=
mstc
->
mstm
;
if
(
mstc
->
port
)
drm_dp_mst_reset_vcpi_slots
(
&
mstm
->
mgr
,
mstc
->
port
);
mstm
->
outp
->
update
(
mstm
->
outp
,
msto
->
head
->
base
.
index
,
NULL
,
0
,
0
);
mstm
->
modified
=
true
;
msto
->
disabled
=
true
;
}
static
const
struct
drm_encoder_helper_funcs
nv50_msto_help
=
{
.
disable
=
nv50_msto_disable
,
.
enable
=
nv50_msto_enable
,
.
atomic_check
=
nv50_msto_atomic_check
,
};
static
void
nv50_msto_destroy
(
struct
drm_encoder
*
encoder
)
{
struct
nv50_msto
*
msto
=
nv50_msto
(
encoder
);
drm_encoder_cleanup
(
&
msto
->
encoder
);
kfree
(
msto
);
}
static
const
struct
drm_encoder_funcs
nv50_msto
=
{
.
destroy
=
nv50_msto_destroy
,
};
static
int
nv50_msto_new
(
struct
drm_device
*
dev
,
u32
heads
,
const
char
*
name
,
int
id
,
struct
nv50_msto
**
pmsto
)
{
struct
nv50_msto
*
msto
;
int
ret
;
if
(
!
(
msto
=
*
pmsto
=
kzalloc
(
sizeof
(
*
msto
),
GFP_KERNEL
)))
return
-
ENOMEM
;
ret
=
drm_encoder_init
(
dev
,
&
msto
->
encoder
,
&
nv50_msto
,
DRM_MODE_ENCODER_DPMST
,
"%s-mst-%d"
,
name
,
id
);
if
(
ret
)
{
kfree
(
*
pmsto
);
*
pmsto
=
NULL
;
return
ret
;
}
drm_encoder_helper_add
(
&
msto
->
encoder
,
&
nv50_msto_help
);
msto
->
encoder
.
possible_crtcs
=
heads
;
return
0
;
}
static
struct
drm_encoder
*
nv50_mstc_atomic_best_encoder
(
struct
drm_connector
*
connector
,
struct
drm_connector_state
*
connector_state
)
{
struct
nv50_head
*
head
=
nv50_head
(
connector_state
->
crtc
);
struct
nv50_mstc
*
mstc
=
nv50_mstc
(
connector
);
if
(
mstc
->
port
)
{
struct
nv50_mstm
*
mstm
=
mstc
->
mstm
;
return
&
mstm
->
msto
[
head
->
base
.
index
]
->
encoder
;
}
return
NULL
;
}
static
struct
drm_encoder
*
nv50_mstc_best_encoder
(
struct
drm_connector
*
connector
)
{
struct
nv50_mstc
*
mstc
=
nv50_mstc
(
connector
);
if
(
mstc
->
port
)
{
struct
nv50_mstm
*
mstm
=
mstc
->
mstm
;
return
&
mstm
->
msto
[
0
]
->
encoder
;
}
return
NULL
;
}
static
enum
drm_mode_status
nv50_mstc_mode_valid
(
struct
drm_connector
*
connector
,
struct
drm_display_mode
*
mode
)
{
return
MODE_OK
;
}
static
int
nv50_mstc_get_modes
(
struct
drm_connector
*
connector
)
{
struct
nv50_mstc
*
mstc
=
nv50_mstc
(
connector
);
int
ret
=
0
;
mstc
->
edid
=
drm_dp_mst_get_edid
(
&
mstc
->
connector
,
mstc
->
port
->
mgr
,
mstc
->
port
);
drm_mode_connector_update_edid_property
(
&
mstc
->
connector
,
mstc
->
edid
);
if
(
mstc
->
edid
)
{
ret
=
drm_add_edid_modes
(
&
mstc
->
connector
,
mstc
->
edid
);
drm_edid_to_eld
(
&
mstc
->
connector
,
mstc
->
edid
);
}
if
(
!
mstc
->
connector
.
display_info
.
bpc
)
mstc
->
connector
.
display_info
.
bpc
=
8
;
if
(
mstc
->
native
)
drm_mode_destroy
(
mstc
->
connector
.
dev
,
mstc
->
native
);
mstc
->
native
=
nouveau_conn_native_mode
(
&
mstc
->
connector
);
return
ret
;
}
static
const
struct
drm_connector_helper_funcs
nv50_mstc_help
=
{
.
get_modes
=
nv50_mstc_get_modes
,
.
mode_valid
=
nv50_mstc_mode_valid
,
.
best_encoder
=
nv50_mstc_best_encoder
,
.
atomic_best_encoder
=
nv50_mstc_atomic_best_encoder
,
};
static
enum
drm_connector_status
nv50_mstc_detect
(
struct
drm_connector
*
connector
,
bool
force
)
{
struct
nv50_mstc
*
mstc
=
nv50_mstc
(
connector
);
if
(
!
mstc
->
port
)
return
connector_status_disconnected
;
return
drm_dp_mst_detect_port
(
connector
,
mstc
->
port
->
mgr
,
mstc
->
port
);
}
static
void
nv50_mstc_destroy
(
struct
drm_connector
*
connector
)
{
struct
nv50_mstc
*
mstc
=
nv50_mstc
(
connector
);
drm_connector_cleanup
(
&
mstc
->
connector
);
kfree
(
mstc
);
}
static
const
struct
drm_connector_funcs
nv50_mstc
=
{
.
dpms
=
drm_atomic_helper_connector_dpms
,
.
reset
=
nouveau_conn_reset
,
.
detect
=
nv50_mstc_detect
,
.
fill_modes
=
drm_helper_probe_single_connector_modes
,
.
set_property
=
drm_atomic_helper_connector_set_property
,
.
destroy
=
nv50_mstc_destroy
,
.
atomic_duplicate_state
=
nouveau_conn_atomic_duplicate_state
,
.
atomic_destroy_state
=
nouveau_conn_atomic_destroy_state
,
.
atomic_set_property
=
nouveau_conn_atomic_set_property
,
.
atomic_get_property
=
nouveau_conn_atomic_get_property
,
};
static
int
nv50_mstc_new
(
struct
nv50_mstm
*
mstm
,
struct
drm_dp_mst_port
*
port
,
const
char
*
path
,
struct
nv50_mstc
**
pmstc
)
{
struct
drm_device
*
dev
=
mstm
->
outp
->
base
.
base
.
dev
;
struct
nv50_mstc
*
mstc
;
int
ret
,
i
;
if
(
!
(
mstc
=
*
pmstc
=
kzalloc
(
sizeof
(
*
mstc
),
GFP_KERNEL
)))
return
-
ENOMEM
;
mstc
->
mstm
=
mstm
;
mstc
->
port
=
port
;
ret
=
drm_connector_init
(
dev
,
&
mstc
->
connector
,
&
nv50_mstc
,
DRM_MODE_CONNECTOR_DisplayPort
);
if
(
ret
)
{
kfree
(
*
pmstc
);
*
pmstc
=
NULL
;
return
ret
;
}
drm_connector_helper_add
(
&
mstc
->
connector
,
&
nv50_mstc_help
);
mstc
->
connector
.
funcs
->
reset
(
&
mstc
->
connector
);
nouveau_conn_attach_properties
(
&
mstc
->
connector
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
mstm
->
msto
)
&&
mstm
->
msto
;
i
++
)
drm_mode_connector_attach_encoder
(
&
mstc
->
connector
,
&
mstm
->
msto
[
i
]
->
encoder
);
drm_object_attach_property
(
&
mstc
->
connector
.
base
,
dev
->
mode_config
.
path_property
,
0
);
drm_object_attach_property
(
&
mstc
->
connector
.
base
,
dev
->
mode_config
.
tile_property
,
0
);
drm_mode_connector_set_path_property
(
&
mstc
->
connector
,
path
);
return
0
;
}
static
void
nv50_mstm_cleanup
(
struct
nv50_mstm
*
mstm
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
mstm
->
outp
->
base
.
base
.
dev
);
struct
drm_encoder
*
encoder
;
int
ret
;
NV_ATOMIC
(
drm
,
"%s: mstm cleanup
\n
"
,
mstm
->
outp
->
base
.
base
.
name
);
ret
=
drm_dp_check_act_status
(
&
mstm
->
mgr
);
ret
=
drm_dp_update_payload_part2
(
&
mstm
->
mgr
);
drm_for_each_encoder
(
encoder
,
mstm
->
outp
->
base
.
base
.
dev
)
{
if
(
encoder
->
encoder_type
==
DRM_MODE_ENCODER_DPMST
)
{
struct
nv50_msto
*
msto
=
nv50_msto
(
encoder
);
struct
nv50_mstc
*
mstc
=
msto
->
mstc
;
if
(
mstc
&&
mstc
->
mstm
==
mstm
)
nv50_msto_cleanup
(
msto
);
}
}
mstm
->
modified
=
false
;
}
static
void
nv50_mstm_prepare
(
struct
nv50_mstm
*
mstm
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
mstm
->
outp
->
base
.
base
.
dev
);
struct
drm_encoder
*
encoder
;
int
ret
;
NV_ATOMIC
(
drm
,
"%s: mstm prepare
\n
"
,
mstm
->
outp
->
base
.
base
.
name
);
ret
=
drm_dp_update_payload_part1
(
&
mstm
->
mgr
);
drm_for_each_encoder
(
encoder
,
mstm
->
outp
->
base
.
base
.
dev
)
{
if
(
encoder
->
encoder_type
==
DRM_MODE_ENCODER_DPMST
)
{
struct
nv50_msto
*
msto
=
nv50_msto
(
encoder
);
struct
nv50_mstc
*
mstc
=
msto
->
mstc
;
if
(
mstc
&&
mstc
->
mstm
==
mstm
)
nv50_msto_prepare
(
msto
);
}
}
}
static
void
nv50_mstm_hotplug
(
struct
drm_dp_mst_topology_mgr
*
mgr
)
{
struct
nv50_mstm
*
mstm
=
nv50_mstm
(
mgr
);
drm_kms_helper_hotplug_event
(
mstm
->
outp
->
base
.
base
.
dev
);
}
static
void
nv50_mstm_destroy_connector
(
struct
drm_dp_mst_topology_mgr
*
mgr
,
struct
drm_connector
*
connector
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
connector
->
dev
);
struct
nv50_mstc
*
mstc
=
nv50_mstc
(
connector
);
drm_connector_unregister
(
&
mstc
->
connector
);
drm_modeset_lock_all
(
drm
->
dev
);
drm_fb_helper_remove_one_connector
(
&
drm
->
fbcon
->
helper
,
&
mstc
->
connector
);
mstc
->
port
=
NULL
;
drm_modeset_unlock_all
(
drm
->
dev
);
drm_connector_unreference
(
&
mstc
->
connector
);
}
static
void
nv50_mstm_register_connector
(
struct
drm_connector
*
connector
)
{
struct
nouveau_drm
*
drm
=
nouveau_drm
(
connector
->
dev
);
drm_modeset_lock_all
(
drm
->
dev
);
drm_fb_helper_add_one_connector
(
&
drm
->
fbcon
->
helper
,
connector
);
drm_modeset_unlock_all
(
drm
->
dev
);
drm_connector_register
(
connector
);
}
static
struct
drm_connector
*
nv50_mstm_add_connector
(
struct
drm_dp_mst_topology_mgr
*
mgr
,
struct
drm_dp_mst_port
*
port
,
const
char
*
path
)
{
struct
nv50_mstm
*
mstm
=
nv50_mstm
(
mgr
);
struct
nv50_mstc
*
mstc
;
int
ret
;
ret
=
nv50_mstc_new
(
mstm
,
port
,
path
,
&
mstc
);
if
(
ret
)
{
if
(
mstc
)
mstc
->
connector
.
funcs
->
destroy
(
&
mstc
->
connector
);
return
NULL
;
}
return
&
mstc
->
connector
;
}
static
const
struct
drm_dp_mst_topology_cbs
nv50_mstm
=
{
.
add_connector
=
nv50_mstm_add_connector
,
.
register_connector
=
nv50_mstm_register_connector
,
.
destroy_connector
=
nv50_mstm_destroy_connector
,
.
hotplug
=
nv50_mstm_hotplug
,
};
void
nv50_mstm_service
(
struct
nv50_mstm
*
mstm
)
{
struct
drm_dp_aux
*
aux
=
mstm
->
mgr
.
aux
;
bool
handled
=
true
;
int
ret
;
u8
esi
[
8
]
=
{};
while
(
handled
)
{
ret
=
drm_dp_dpcd_read
(
aux
,
DP_SINK_COUNT_ESI
,
esi
,
8
);
if
(
ret
!=
8
)
{
drm_dp_mst_topology_mgr_set_mst
(
&
mstm
->
mgr
,
false
);
return
;
}
drm_dp_mst_hpd_irq
(
&
mstm
->
mgr
,
esi
,
&
handled
);
if
(
!
handled
)
break
;
drm_dp_dpcd_write
(
aux
,
DP_SINK_COUNT_ESI
+
1
,
&
esi
[
1
],
3
);
}
}
void
nv50_mstm_remove
(
struct
nv50_mstm
*
mstm
)
{
if
(
mstm
)
drm_dp_mst_topology_mgr_set_mst
(
&
mstm
->
mgr
,
false
);
}
static
int
static
int
nv50_mstm_enable
(
struct
nv50_mstm
*
mstm
,
u8
dpcd
,
int
state
)
nv50_mstm_enable
(
struct
nv50_mstm
*
mstm
,
u8
dpcd
,
int
state
)
{
{
...
@@ -2872,6 +3362,20 @@ nv50_mstm_detect(struct nv50_mstm *mstm, u8 dpcd[8], int allow)
...
@@ -2872,6 +3362,20 @@ nv50_mstm_detect(struct nv50_mstm *mstm, u8 dpcd[8], int allow)
return
mstm
->
mgr
.
mst_state
;
return
mstm
->
mgr
.
mst_state
;
}
}
static
void
nv50_mstm_fini
(
struct
nv50_mstm
*
mstm
)
{
if
(
mstm
&&
mstm
->
mgr
.
mst_state
)
drm_dp_mst_topology_mgr_suspend
(
&
mstm
->
mgr
);
}
static
void
nv50_mstm_init
(
struct
nv50_mstm
*
mstm
)
{
if
(
mstm
&&
mstm
->
mgr
.
mst_state
)
drm_dp_mst_topology_mgr_resume
(
&
mstm
->
mgr
);
}
static
void
static
void
nv50_mstm_del
(
struct
nv50_mstm
**
pmstm
)
nv50_mstm_del
(
struct
nv50_mstm
**
pmstm
)
{
{
...
@@ -2889,17 +3393,36 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max,
...
@@ -2889,17 +3393,36 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max,
const
int
max_payloads
=
hweight8
(
outp
->
dcb
->
heads
);
const
int
max_payloads
=
hweight8
(
outp
->
dcb
->
heads
);
struct
drm_device
*
dev
=
outp
->
base
.
base
.
dev
;
struct
drm_device
*
dev
=
outp
->
base
.
base
.
dev
;
struct
nv50_mstm
*
mstm
;
struct
nv50_mstm
*
mstm
;
int
ret
;
int
ret
,
i
;
u8
dpcd
;
/* This is a workaround for some monitors not functioning
* correctly in MST mode on initial module load. I think
* some bad interaction with the VBIOS may be responsible.
*
* A good ol' off and on again seems to work here ;)
*/
ret
=
drm_dp_dpcd_readb
(
aux
,
DP_DPCD_REV
,
&
dpcd
);
if
(
ret
>=
0
&&
dpcd
>=
0x12
)
drm_dp_dpcd_writeb
(
aux
,
DP_MSTM_CTRL
,
0
);
if
(
!
(
mstm
=
*
pmstm
=
kzalloc
(
sizeof
(
*
mstm
),
GFP_KERNEL
)))
if
(
!
(
mstm
=
*
pmstm
=
kzalloc
(
sizeof
(
*
mstm
),
GFP_KERNEL
)))
return
-
ENOMEM
;
return
-
ENOMEM
;
mstm
->
outp
=
outp
;
mstm
->
outp
=
outp
;
mstm
->
mgr
.
cbs
=
&
nv50_mstm
;
ret
=
drm_dp_mst_topology_mgr_init
(
&
mstm
->
mgr
,
dev
->
dev
,
aux
,
aux_max
,
ret
=
drm_dp_mst_topology_mgr_init
(
&
mstm
->
mgr
,
dev
->
dev
,
aux
,
aux_max
,
max_payloads
,
conn_base_id
);
max_payloads
,
conn_base_id
);
if
(
ret
)
if
(
ret
)
return
ret
;
return
ret
;
for
(
i
=
0
;
i
<
max_payloads
;
i
++
)
{
ret
=
nv50_msto_new
(
dev
,
outp
->
dcb
->
heads
,
outp
->
base
.
base
.
name
,
i
,
&
mstm
->
msto
[
i
]);
if
(
ret
)
return
ret
;
}
return
0
;
return
0
;
}
}
...
@@ -3364,10 +3887,20 @@ nv50_disp_atomic_commit_core(struct nouveau_drm *drm, u32 interlock)
...
@@ -3364,10 +3887,20 @@ nv50_disp_atomic_commit_core(struct nouveau_drm *drm, u32 interlock)
{
{
struct
nv50_disp
*
disp
=
nv50_disp
(
drm
->
dev
);
struct
nv50_disp
*
disp
=
nv50_disp
(
drm
->
dev
);
struct
nv50_dmac
*
core
=
&
disp
->
mast
.
base
;
struct
nv50_dmac
*
core
=
&
disp
->
mast
.
base
;
struct
nv50_mstm
*
mstm
;
struct
drm_encoder
*
encoder
;
u32
*
push
;
u32
*
push
;
NV_ATOMIC
(
drm
,
"commit core %08x
\n
"
,
interlock
);
NV_ATOMIC
(
drm
,
"commit core %08x
\n
"
,
interlock
);
drm_for_each_encoder
(
encoder
,
drm
->
dev
)
{
if
(
encoder
->
encoder_type
!=
DRM_MODE_ENCODER_DPMST
)
{
mstm
=
nouveau_encoder
(
encoder
)
->
dp
.
mstm
;
if
(
mstm
&&
mstm
->
modified
)
nv50_mstm_prepare
(
mstm
);
}
}
if
((
push
=
evo_wait
(
core
,
5
)))
{
if
((
push
=
evo_wait
(
core
,
5
)))
{
evo_mthd
(
push
,
0x0084
,
1
);
evo_mthd
(
push
,
0x0084
,
1
);
evo_data
(
push
,
0x80000000
);
evo_data
(
push
,
0x80000000
);
...
@@ -3383,6 +3916,14 @@ nv50_disp_atomic_commit_core(struct nouveau_drm *drm, u32 interlock)
...
@@ -3383,6 +3916,14 @@ nv50_disp_atomic_commit_core(struct nouveau_drm *drm, u32 interlock)
)
<
0
)
)
<
0
)
NV_ERROR
(
drm
,
"EVO timeout
\n
"
);
NV_ERROR
(
drm
,
"EVO timeout
\n
"
);
}
}
drm_for_each_encoder
(
encoder
,
drm
->
dev
)
{
if
(
encoder
->
encoder_type
!=
DRM_MODE_ENCODER_DPMST
)
{
mstm
=
nouveau_encoder
(
encoder
)
->
dp
.
mstm
;
if
(
mstm
&&
mstm
->
modified
)
nv50_mstm_cleanup
(
mstm
);
}
}
}
}
static
void
static
void
...
@@ -3792,6 +4333,8 @@ nv50_disp_func = {
...
@@ -3792,6 +4333,8 @@ nv50_disp_func = {
void
void
nv50_display_fini
(
struct
drm_device
*
dev
)
nv50_display_fini
(
struct
drm_device
*
dev
)
{
{
struct
nouveau_encoder
*
nv_encoder
;
struct
drm_encoder
*
encoder
;
struct
drm_plane
*
plane
;
struct
drm_plane
*
plane
;
drm_for_each_plane
(
plane
,
dev
)
{
drm_for_each_plane
(
plane
,
dev
)
{
...
@@ -3800,6 +4343,13 @@ nv50_display_fini(struct drm_device *dev)
...
@@ -3800,6 +4343,13 @@ nv50_display_fini(struct drm_device *dev)
continue
;
continue
;
nv50_wndw_fini
(
wndw
);
nv50_wndw_fini
(
wndw
);
}
}
list_for_each_entry
(
encoder
,
&
dev
->
mode_config
.
encoder_list
,
head
)
{
if
(
encoder
->
encoder_type
!=
DRM_MODE_ENCODER_DPMST
)
{
nv_encoder
=
nouveau_encoder
(
encoder
);
nv50_mstm_fini
(
nv_encoder
->
dp
.
mstm
);
}
}
}
}
int
int
...
@@ -3827,6 +4377,8 @@ nv50_display_init(struct drm_device *dev)
...
@@ -3827,6 +4377,8 @@ nv50_display_init(struct drm_device *dev)
help
=
encoder
->
helper_private
;
help
=
encoder
->
helper_private
;
if
(
help
&&
help
->
dpms
)
if
(
help
&&
help
->
dpms
)
help
->
dpms
(
encoder
,
DRM_MODE_DPMS_ON
);
help
->
dpms
(
encoder
,
DRM_MODE_DPMS_ON
);
nv50_mstm_init
(
nv_encoder
->
dp
.
mstm
);
}
}
}
}
...
...
Kirill Smelkov
@kirr
mentioned in commit
27a451e8
·
Sep 27, 2017
mentioned in commit
27a451e8
mentioned in commit 27a451e83e78e1167f58cddba189fae4b4fa0d5b
Toggle commit list
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