446 lines
14 KiB
Ada
446 lines
14 KiB
Ada
|
--
|
||
|
-- Copyright (C) 2015-2017 secunet Security Networks AG
|
||
|
--
|
||
|
-- This program is free software; you can redistribute it and/or modify
|
||
|
-- it under the terms of the GNU General Public License as published by
|
||
|
-- the Free Software Foundation; either version 2 of the License, or
|
||
|
-- (at your option) any later version.
|
||
|
--
|
||
|
-- This program is distributed in the hope that it will be useful,
|
||
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
-- GNU General Public License for more details.
|
||
|
--
|
||
|
|
||
|
with Ada.Unchecked_Conversion;
|
||
|
|
||
|
with HW.Debug;
|
||
|
with GNAT.Source_Info;
|
||
|
|
||
|
with HW.GFX.DP_Defs;
|
||
|
|
||
|
use type HW.Word8;
|
||
|
use type HW.Word16;
|
||
|
|
||
|
package body HW.GFX.DP_Info is
|
||
|
|
||
|
procedure Read_Caps
|
||
|
(Link : in out DP_Link;
|
||
|
Port : in T;
|
||
|
Success : out Boolean)
|
||
|
is
|
||
|
Data : DP_Defs.Aux_Payload;
|
||
|
Length : DP_Defs.Aux_Payload_Length;
|
||
|
|
||
|
Caps_Size : constant := 15;
|
||
|
begin
|
||
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
||
|
|
||
|
Length := Caps_Size;
|
||
|
Aux_Ch.Aux_Read
|
||
|
(Port => Port,
|
||
|
Address => 16#00000#,
|
||
|
Length => Length,
|
||
|
Data => Data,
|
||
|
Success => Success);
|
||
|
Success := Success and Length = Caps_Size;
|
||
|
|
||
|
if Length = Caps_Size then
|
||
|
Link.Receiver_Caps.Rev := Data (0);
|
||
|
case Data (1) is
|
||
|
when 16#06# =>
|
||
|
Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_1_62;
|
||
|
when 16#0a# =>
|
||
|
Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_2_7;
|
||
|
when 16#14# =>
|
||
|
Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_5_4;
|
||
|
when others =>
|
||
|
if Data (1) > 16#14# then
|
||
|
Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_5_4;
|
||
|
else
|
||
|
Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_1_62;
|
||
|
end if;
|
||
|
end case;
|
||
|
case Data (2) and 16#1f# is
|
||
|
when 0 | 1 =>
|
||
|
Link.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_1;
|
||
|
when 2 | 3 =>
|
||
|
Link.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_2;
|
||
|
when others =>
|
||
|
Link.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_4;
|
||
|
end case;
|
||
|
Link.Receiver_Caps.TPS3_Supported := (Data (2) and 16#40#) /= 0;
|
||
|
Link.Receiver_Caps.Enhanced_Framing := (Data (2) and 16#80#) /= 0;
|
||
|
Link.Receiver_Caps.No_Aux_Handshake := (Data (3) and 16#40#) /= 0;
|
||
|
Link.Receiver_Caps.Aux_RD_Interval := Data (14);
|
||
|
|
||
|
pragma Debug (Debug.New_Line);
|
||
|
pragma Debug (Debug.Put_Line ("DPCD:"));
|
||
|
pragma Debug (Debug.Put_Reg8 (" Rev ", Data (0)));
|
||
|
pragma Debug (Debug.Put_Reg8 (" Max_Link_Rate ", Data (1)));
|
||
|
pragma Debug (Debug.Put_Reg8 (" Max_Lane_Count ", Data (2) and 16#1f#));
|
||
|
pragma Debug (Debug.Put_Reg8 (" TPS3_Supported ", Data (2) and 16#40#));
|
||
|
pragma Debug (Debug.Put_Reg8 (" Enhanced_Framing", Data (2) and 16#80#));
|
||
|
pragma Debug (Debug.Put_Reg8 (" No_Aux_Handshake", Data (3) and 16#40#));
|
||
|
pragma Debug (Debug.Put_Reg8 (" Aux_RD_Interval ", Data (14)));
|
||
|
pragma Debug (Debug.New_Line);
|
||
|
end if;
|
||
|
end Read_Caps;
|
||
|
|
||
|
function Read_LE16
|
||
|
(Raw_Payload : DP_Defs.Aux_Payload;
|
||
|
Offset : DP_Defs.Aux_Payload_Index)
|
||
|
return Word16
|
||
|
with
|
||
|
Pre => Offset < DP_Defs.Aux_Payload_Index'Last
|
||
|
is
|
||
|
begin
|
||
|
return Shift_Left (Word16 (Raw_Payload (Offset + 1)), 8) or
|
||
|
Word16 (Raw_Payload (Offset));
|
||
|
end Read_LE16;
|
||
|
|
||
|
procedure Read_eDP_Rates
|
||
|
(Link : in out DP_Link;
|
||
|
Port : in T)
|
||
|
is
|
||
|
Data : DP_Defs.Aux_Payload;
|
||
|
Length : DP_Defs.Aux_Payload_Length;
|
||
|
Max_Rate : Natural;
|
||
|
Success : Boolean;
|
||
|
|
||
|
Caps_Size : constant := 3;
|
||
|
Rates_Size : constant := 16;
|
||
|
eDP_Rev_1_4 : constant := 3;
|
||
|
begin
|
||
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
||
|
|
||
|
Length := Caps_Size;
|
||
|
Aux_Ch.Aux_Read
|
||
|
(Port => Port,
|
||
|
Address => 16#00700#,
|
||
|
Length => Length,
|
||
|
Data => Data,
|
||
|
Success => Success);
|
||
|
Success := Success and Length = Caps_Size;
|
||
|
if not Success or Data (0) < eDP_Rev_1_4 then
|
||
|
return;
|
||
|
end if;
|
||
|
|
||
|
Length := Rates_Size;
|
||
|
Aux_Ch.Aux_Read
|
||
|
(Port => Port,
|
||
|
Address => 16#00010#,
|
||
|
Length => Length,
|
||
|
Data => Data,
|
||
|
Success => Success);
|
||
|
Success := Success and Length = Rates_Size;
|
||
|
if not Success then
|
||
|
return;
|
||
|
end if;
|
||
|
|
||
|
pragma Debug (Debug.New_Line);
|
||
|
pragma Debug (Debug.Put_Line ("DPCD eDP 1.4+ link rates:"));
|
||
|
|
||
|
-- TODO: support individual link rates (storing them all)
|
||
|
|
||
|
Max_Rate := Natural'First;
|
||
|
for I in 0 .. Length / 2 - 1 loop
|
||
|
pragma Loop_Invariant (Max_Rate <= Natural (Word16'Last));
|
||
|
declare
|
||
|
Rate : constant Word16 := Read_LE16 (Data, I * 2);
|
||
|
begin
|
||
|
exit when Rate = 0; -- prematurely exit the loop
|
||
|
pragma Debug (Debug.Put (" - "));
|
||
|
pragma Debug (Debug.Put_Int8 (Int8 (I)));
|
||
|
pragma Debug (Debug.Put (": "));
|
||
|
pragma Debug (Debug.Put_Word16 (Rate));
|
||
|
pragma Debug (Debug.New_Line);
|
||
|
if Natural (Rate) > Max_Rate then
|
||
|
Max_Rate := Natural (Rate);
|
||
|
end if;
|
||
|
end;
|
||
|
end loop;
|
||
|
pragma Debug (Debug.New_Line);
|
||
|
|
||
|
-- convert to KHz
|
||
|
Max_Rate := Max_Rate * 20;
|
||
|
|
||
|
if Max_Rate >= 540_000 then
|
||
|
Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_5_4;
|
||
|
elsif Max_Rate >= 270_000 then
|
||
|
Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_2_7;
|
||
|
elsif Max_Rate >= 162_000 then
|
||
|
Link.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_1_62;
|
||
|
end if;
|
||
|
end Read_eDP_Rates;
|
||
|
|
||
|
procedure Minimum_Lane_Count
|
||
|
(Link : in out DP_Link;
|
||
|
Mode : in Mode_Type;
|
||
|
Success : out Boolean)
|
||
|
with
|
||
|
Depends => ((Link, Success) => (Link, Mode))
|
||
|
is
|
||
|
Lane_Pixel_Per_Second : constant Pos64 :=
|
||
|
(((DP_Symbol_Rate (Link.Bandwidth) * 8) / 3) / Mode.BPC);
|
||
|
Count : constant Pos64 :=
|
||
|
Div_Round_Up (Mode.Dotclock, Lane_Pixel_Per_Second);
|
||
|
begin
|
||
|
Success := True;
|
||
|
case Count is
|
||
|
when 1 => Link.Lane_Count := DP_Lane_Count_1;
|
||
|
when 2 => Link.Lane_Count := DP_Lane_Count_2;
|
||
|
when 3 | 4 => Link.Lane_Count := DP_Lane_Count_4;
|
||
|
when others => Success := False;
|
||
|
end case;
|
||
|
end Minimum_Lane_Count;
|
||
|
|
||
|
procedure Preferred_Link_Setting
|
||
|
(Link : in out DP_Link;
|
||
|
Mode : in Mode_Type;
|
||
|
Success : out Boolean)
|
||
|
is
|
||
|
begin
|
||
|
Link.Bandwidth := Link.Receiver_Caps.Max_Link_Rate;
|
||
|
Link.Enhanced_Framing := Link.Receiver_Caps.Enhanced_Framing;
|
||
|
|
||
|
Minimum_Lane_Count (Link, Mode, Success);
|
||
|
|
||
|
Success := Success and
|
||
|
Link.Lane_Count <= Link.Receiver_Caps.Max_Lane_Count;
|
||
|
|
||
|
pragma Debug (not Success, Debug.Put_Line
|
||
|
("Mode requirements exceed available bandwidth!"));
|
||
|
end Preferred_Link_Setting;
|
||
|
|
||
|
procedure Next_Link_Setting
|
||
|
(Link : in out DP_Link;
|
||
|
Mode : in Mode_Type;
|
||
|
Success : out Boolean)
|
||
|
is
|
||
|
begin
|
||
|
if Link.Bandwidth > DP_Bandwidth'First then
|
||
|
Link.Bandwidth := DP_Bandwidth'Pred (Link.Bandwidth);
|
||
|
|
||
|
Minimum_Lane_Count (Link, Mode, Success);
|
||
|
|
||
|
Success := Success and
|
||
|
Link.Lane_Count <= Link.Receiver_Caps.Max_Lane_Count;
|
||
|
else
|
||
|
Success := False;
|
||
|
end if;
|
||
|
end Next_Link_Setting;
|
||
|
|
||
|
procedure Dump_Link_Setting (Link : DP_Link)
|
||
|
is
|
||
|
begin
|
||
|
Debug.Put ("Trying DP settings: Symbol Rate = ");
|
||
|
Debug.Put_Int32 (Int32 (DP_Symbol_Rate (Link.Bandwidth)));
|
||
|
Debug.Put ("; Lane Count = ");
|
||
|
Debug.Put_Int32 (Int32 (Lane_Count_As_Integer (Link.Lane_Count)));
|
||
|
Debug.New_Line;
|
||
|
Debug.New_Line;
|
||
|
end Dump_Link_Setting;
|
||
|
|
||
|
----------------------------------------------------------------------------
|
||
|
|
||
|
procedure Calculate_M_N
|
||
|
(Link : in DP_Link;
|
||
|
Mode : in Mode_Type;
|
||
|
Data_M : out M_Type;
|
||
|
Data_N : out N_Type;
|
||
|
Link_M : out M_Type;
|
||
|
Link_N : out N_Type)
|
||
|
is
|
||
|
DATA_N_MAX : constant := 16#800000#;
|
||
|
LINK_N_MAX : constant := 16#100000#;
|
||
|
|
||
|
subtype Calc_M_Type is Int64 range 0 .. 2 ** 38;
|
||
|
subtype Calc_N_Type is Int64 range 0 .. 2 ** 38;
|
||
|
subtype N_Rounded_Type is Int64 range
|
||
|
0 .. Int64'Max (DATA_N_MAX, LINK_N_MAX);
|
||
|
|
||
|
M : Calc_M_Type;
|
||
|
N : Calc_N_Type;
|
||
|
|
||
|
procedure Cancel_M_N
|
||
|
(M : in out Calc_M_Type;
|
||
|
N : in out Calc_N_Type;
|
||
|
N_Max : in N_Rounded_Type)
|
||
|
with
|
||
|
Depends => ((M, N) => (M, N, N_max)),
|
||
|
Pre => (N > 0 and M in 0 .. Calc_M_Type'Last / 2),
|
||
|
Post => (M <= M_N_Max and N <= M_N_Max)
|
||
|
is
|
||
|
Orig_N : constant Calc_N_Type := N;
|
||
|
|
||
|
function Round_N (N : Calc_N_Type) return N_Rounded_Type
|
||
|
with
|
||
|
Post => (Round_N'Result <= N * 2)
|
||
|
is
|
||
|
RN : Calc_N_Type;
|
||
|
RN2 : Calc_N_Type := N_Max;
|
||
|
begin
|
||
|
loop
|
||
|
RN := RN2;
|
||
|
RN2 := RN2 / 2;
|
||
|
exit when RN2 < N;
|
||
|
pragma Loop_Invariant (RN2 = RN / 2 and RN2 in N .. N_Max);
|
||
|
end loop;
|
||
|
return RN;
|
||
|
end Round_N;
|
||
|
begin
|
||
|
N := Round_N (N);
|
||
|
|
||
|
-- The automatic provers need a little nudge here.
|
||
|
pragma Assert
|
||
|
(if M <= Calc_M_Type'Last/2 and
|
||
|
N <= Orig_N * 2 and
|
||
|
Orig_N > 0 and
|
||
|
M > 0
|
||
|
then
|
||
|
M * N / Orig_N <= Calc_M_Type'Last);
|
||
|
|
||
|
pragma Annotate (GNATprove, False_Positive,
|
||
|
"assertion might fail",
|
||
|
"The property cannot be proven automatically. An Isabelle proof is included as an axiom");
|
||
|
|
||
|
M := M * N / Orig_N;
|
||
|
|
||
|
-- This loop is never hit for sane values (i.e. M <= N) but
|
||
|
-- we have to make sure returned values are always in range.
|
||
|
while M > M_N_Max loop
|
||
|
pragma Loop_Invariant (N <= M_N_Max);
|
||
|
M := M / 2;
|
||
|
N := N / 2;
|
||
|
end loop;
|
||
|
end Cancel_M_N;
|
||
|
begin
|
||
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
||
|
|
||
|
pragma Assert (3
|
||
|
* Mode.BPC
|
||
|
* Mode.Dotclock
|
||
|
in Pos64);
|
||
|
M := 3
|
||
|
* Mode.BPC
|
||
|
* Mode.Dotclock;
|
||
|
|
||
|
pragma Assert (8
|
||
|
* DP_Symbol_Rate (Link.Bandwidth)
|
||
|
* Lane_Count_As_Integer (Link.Lane_Count)
|
||
|
in Pos64);
|
||
|
N := 8
|
||
|
* DP_Symbol_Rate (Link.Bandwidth)
|
||
|
* Lane_Count_As_Integer (Link.Lane_Count);
|
||
|
|
||
|
Cancel_M_N (M, N, DATA_N_MAX);
|
||
|
Data_M := M;
|
||
|
Data_N := N;
|
||
|
|
||
|
-------------------------------------------------------------------
|
||
|
|
||
|
M := Pos64 (Mode.Dotclock);
|
||
|
N := Pos64 (DP_Symbol_Rate (Link.Bandwidth));
|
||
|
|
||
|
Cancel_M_N (M, N, LINK_N_MAX);
|
||
|
Link_M := M;
|
||
|
Link_N := N;
|
||
|
end Calculate_M_N;
|
||
|
|
||
|
----------------------------------------------------------------------------
|
||
|
|
||
|
procedure Read_Link_Status
|
||
|
(Port : in T;
|
||
|
Status : out Link_Status;
|
||
|
Success : out Boolean)
|
||
|
is
|
||
|
subtype Status_Index is DP_Defs.Aux_Payload_Index range 0 .. 5;
|
||
|
subtype Status_Buffer is Buffer (Status_Index);
|
||
|
function Buffer_As_Status is new Ada.Unchecked_Conversion
|
||
|
(Source => Status_Buffer, Target => Link_Status);
|
||
|
|
||
|
Data : DP_Defs.Aux_Payload;
|
||
|
Length : DP_Defs.Aux_Payload_Length;
|
||
|
begin
|
||
|
pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
|
||
|
|
||
|
Length := Status_Index'Last + 1;
|
||
|
Aux_Ch.Aux_Read
|
||
|
(Port => Port,
|
||
|
Address => 16#00202#,
|
||
|
Length => Length,
|
||
|
Data => Data,
|
||
|
Success => Success);
|
||
|
Success := Success and Length = Status_Index'Last + 1;
|
||
|
Status := Buffer_As_Status (Data (Status_Index));
|
||
|
end Read_Link_Status;
|
||
|
|
||
|
function All_CR_Done
|
||
|
(Status : Link_Status;
|
||
|
Link : DP_Link)
|
||
|
return Boolean
|
||
|
is
|
||
|
CR_Done : Boolean := True;
|
||
|
begin
|
||
|
for Lane in Lane_Index
|
||
|
range 0 .. Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
|
||
|
loop
|
||
|
CR_Done := CR_Done and Status.Lanes (Lane).CR_Done;
|
||
|
end loop;
|
||
|
return CR_Done;
|
||
|
end All_CR_Done;
|
||
|
|
||
|
function All_EQ_Done
|
||
|
(Status : Link_Status;
|
||
|
Link : DP_Link)
|
||
|
return Boolean
|
||
|
is
|
||
|
EQ_Done : Boolean := True;
|
||
|
begin
|
||
|
for Lane in Lane_Index
|
||
|
range 0 .. Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
|
||
|
loop
|
||
|
EQ_Done := EQ_Done and Status.Lanes (Lane).CR_Done
|
||
|
and Status.Lanes (Lane).Channel_EQ_Done
|
||
|
and Status.Lanes (Lane).Symbol_Locked;
|
||
|
end loop;
|
||
|
return EQ_Done and Status.Interlane_Align_Done;
|
||
|
end All_EQ_Done;
|
||
|
|
||
|
function Max_Requested_VS
|
||
|
(Status : Link_Status;
|
||
|
Link : DP_Link)
|
||
|
return DP_Voltage_Swing
|
||
|
is
|
||
|
VS : DP_Voltage_Swing := DP_Voltage_Swing'First;
|
||
|
begin
|
||
|
for Lane in Lane_Index
|
||
|
range 0 .. Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
|
||
|
loop
|
||
|
if Status.Adjust_Requests (Lane).Voltage_Swing > VS then
|
||
|
VS := Status.Adjust_Requests (Lane).Voltage_Swing;
|
||
|
end if;
|
||
|
end loop;
|
||
|
return VS;
|
||
|
end Max_Requested_VS;
|
||
|
|
||
|
function Max_Requested_Emph
|
||
|
(Status : Link_Status;
|
||
|
Link : DP_Link)
|
||
|
return DP_Pre_Emph
|
||
|
is
|
||
|
Emph : DP_Pre_Emph := DP_Pre_Emph'First;
|
||
|
begin
|
||
|
for Lane in Lane_Index
|
||
|
range 0 .. Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
|
||
|
loop
|
||
|
if Status.Adjust_Requests (Lane).Pre_Emph > Emph then
|
||
|
Emph := Status.Adjust_Requests (Lane).Pre_Emph;
|
||
|
end if;
|
||
|
end loop;
|
||
|
return Emph;
|
||
|
end Max_Requested_Emph;
|
||
|
|
||
|
end HW.GFX.DP_Info;
|