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
186ecad2
Commit
186ecad2
authored
Nov 09, 2012
by
Ben Skeggs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
drm/nv50/disp: move remaining interrupt handling into core
Signed-off-by:
Ben Skeggs
<
bskeggs@redhat.com
>
parent
4a230fa6
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
461 additions
and
576 deletions
+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
No files found.
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
View file @
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
View file @
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
View file @
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
View file @
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
View file @
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
)
{
...
...
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