Initial upstream commit
The history contained unlicensed code so everything got squashed, sorry.
Change-Id: I9f5775208f9df6fb29074bf3bc498f68cb17b3a0
Signed-off-by: Nico Huber <nico.huber@secunet.com>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..69f6818
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/proof-allconfigs/
+/build/
+/dest/
+/*.gpr
+/.config
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f9e358f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+name := gfxinit
+
+gfxinit-deplibs := libhw
+
+libhw-dir ?= ../libhwbase/dest
+include $(libhw-dir)/Makefile
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 0000000..6eeafb9
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1 @@
+subdirs-y += common
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..7efa9e1
--- /dev/null
+++ b/TODO
@@ -0,0 +1,5 @@
+medium DP re-read status if updated bit isn't set
+medium DP honor adjust requests also if training succeeded?
+unknown DP honor SINK_STATUS after training
+
+low LVDS sync polarity, 8bit colors, data format???
diff --git a/common/Makefile.inc b/common/Makefile.inc
new file mode 100644
index 0000000..8855e8a
--- /dev/null
+++ b/common/Makefile.inc
@@ -0,0 +1,57 @@
+gfxinit-y += hw-gfx-dp_aux_ch.adb
+gfxinit-y += hw-gfx-dp_aux_ch.ads
+gfxinit-y += hw-gfx-dp_defs.ads
+gfxinit-y += hw-gfx-dp_info.adb
+gfxinit-y += hw-gfx-dp_info.ads
+gfxinit-y += hw-gfx-dp_training.adb
+gfxinit-y += hw-gfx-dp_training.ads
+gfxinit-y += hw-gfx-edid.adb
+gfxinit-y += hw-gfx-edid.ads
+gfxinit-y += hw-gfx-gma-connector_info.adb
+gfxinit-y += hw-gfx-gma-connector_info.ads
+gfxinit-y += hw-gfx-gma-connectors.ads
+gfxinit-y += hw-gfx-gma-dp_aux_ch.ads
+gfxinit-y += hw-gfx-gma-dp_aux_request.adb
+gfxinit-y += hw-gfx-gma-dp_aux_request.ads
+gfxinit-y += hw-gfx-gma-dp_info.ads
+gfxinit-y += hw-gfx-gma-i2c.adb
+gfxinit-y += hw-gfx-gma-i2c.ads
+gfxinit-y += hw-gfx-gma-panel.adb
+gfxinit-y += hw-gfx-gma-panel.ads
+gfxinit-y += hw-gfx-gma-pch-fdi.adb
+gfxinit-y += hw-gfx-gma-pch-fdi.ads
+gfxinit-y += hw-gfx-gma-pch-sideband.adb
+gfxinit-y += hw-gfx-gma-pch-sideband.ads
+gfxinit-y += hw-gfx-gma-pch-transcoder.adb
+gfxinit-y += hw-gfx-gma-pch-transcoder.ads
+gfxinit-y += hw-gfx-gma-pch-vga.adb
+gfxinit-y += hw-gfx-gma-pch-vga.ads
+gfxinit-y += hw-gfx-gma-pch.ads
+gfxinit-y += hw-gfx-gma-pipe_setup.adb
+gfxinit-y += hw-gfx-gma-pipe_setup.ads
+gfxinit-y += hw-gfx-gma-port_detect.ads
+gfxinit-y += hw-gfx-gma-registers.adb
+gfxinit-y += hw-gfx-gma-registers.ads
+gfxinit-y += hw-gfx-gma.adb
+gfxinit-y += hw-gfx-gma.ads
+gfxinit-y += hw-gfx-i2c.ads
+gfxinit-y += hw-gfx.ads
+gfxinit-$(CONFIG_HWBASE_DYNAMIC_MMIO) += hw-gfx-framebuffer_filler.adb
+gfxinit-$(CONFIG_HWBASE_DYNAMIC_MMIO) += hw-gfx-framebuffer_filler.ads
+
+$(call src-to-obj,,$(dir)/hw-gfx-gma-config).ads: $(dir)/hw-gfx-gma-config.ads.template $(cnf)
+ printf " GENERATE $(patsubst /%,%,$(subst $(obj)/,,$@))\n"
+ sed -e's/<<CPU>>/$(CONFIG_GFX_GMA_CPU)/' \
+ -e's/<<CPU_VARIANT>>/$(CONFIG_GFX_GMA_CPU_VARIANT)/' \
+ -e's/<<INTERNAL_PORT>>/$(CONFIG_GFX_GMA_INTERNAL_PORT)/' \
+ -e's/<<DEFAULT_MMIO_BASE>>/$(CONFIG_GFX_GMA_DEFAULT_MMIO)/' \
+ $< >$@
+gfxinit-gen-y += $(call src-to-obj,,$(dir)/hw-gfx-gma-config).ads
+
+ifneq ($(filter Ironlake Sandybridge Ivybridge,$(CONFIG_GFX_GMA_CPU)),)
+subdirs-y += ironlake
+else ifneq ($(filter Haswell Broadwell,$(CONFIG_GFX_GMA_CPU)),)
+subdirs-y += haswell_shared haswell
+else
+subdirs-y += haswell_shared skylake
+endif
diff --git a/common/haswell/Makefile.inc b/common/haswell/Makefile.inc
new file mode 100644
index 0000000..682af33
--- /dev/null
+++ b/common/haswell/Makefile.inc
@@ -0,0 +1,8 @@
+gfxinit-y += hw-gfx-gma-plls-lcpll.ads
+gfxinit-y += hw-gfx-gma-plls-wrpll.adb
+gfxinit-y += hw-gfx-gma-plls-wrpll.ads
+gfxinit-y += hw-gfx-gma-plls.adb
+gfxinit-y += hw-gfx-gma-plls.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks.ads
+gfxinit-y += hw-gfx-gma-spll.adb
+gfxinit-y += hw-gfx-gma-spll.ads
diff --git a/common/haswell/hw-gfx-gma-plls-lcpll.ads b/common/haswell/hw-gfx-gma-plls-lcpll.ads
new file mode 100644
index 0000000..7fa45ba
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls-lcpll.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs.LCPLL
+is
+
+ type Fixed_LCPLLs_Array is array (HW.GFX.DP_Bandwidth) of LCPLLs;
+
+ Fixed_LCPLLs : constant Fixed_LCPLLs_Array := Fixed_LCPLLs_Array'
+ (DP_Bandwidth_5_4 => LCPLL0,
+ DP_Bandwidth_2_7 => LCPLL1,
+ DP_Bandwidth_1_62 => LCPLL2);
+
+ type Value_Array is array (LCPLLs) of Word32;
+ Register_Value : constant Value_Array := Value_Array'
+ (LCPLL0 => 0 * 2 ** 29, LCPLL1 => 1 * 2 ** 29, LCPLL2 => 2 * 2 ** 29);
+
+end HW.GFX.GMA.PLLs.LCPLL;
diff --git a/common/haswell/hw-gfx-gma-plls-wrpll.adb b/common/haswell/hw-gfx-gma-plls-wrpll.adb
new file mode 100644
index 0000000..c9ab532
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls-wrpll.adb
@@ -0,0 +1,351 @@
+--
+-- Copyright (C) 2015 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PLLs.WRPLL is
+
+ ----------------------------------------------------------------------------
+ --
+ -- Divider calculation as found in Linux' i915 driver
+ --
+ -- Copyright (C) 2012 Intel Corporation
+ --
+ -- Permission is hereby granted, free of charge, to any person obtaining a
+ -- copy of this software and associated documentation files (the "Software"),
+ -- to deal in the Software without restriction, including without limitation
+ -- the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ -- and/or sell copies of the Software, and to permit persons to whom the
+ -- Software is furnished to do so, subject to the following conditions:
+ --
+ -- The above copyright notice and this permission notice (including the next
+ -- paragraph) shall be included in all copies or substantial portions of the
+ -- Software.
+ --
+ -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ -- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ -- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ -- IN THE SOFTWARE.
+ --
+ -- Authors:
+ -- Eugeni Dodonov <eugeni.dodonov@intel.com>
+ --
+
+ LC_FREQ : constant := 2700; -- in MHz
+ LC_FREQ_2K : constant := LC_FREQ * 2000; -- in 500Hz
+
+ P_MIN : constant := 2;
+ P_MAX : constant := 62; -- i915 says 64, but this would overflow 6-bit
+ P_INC : constant := 2;
+
+ -- Constraints for PLL good behavior
+ REF_MIN : constant := 48;
+ REF_MAX : constant := 400;
+ VCO_MIN : constant := 2400;
+ VCO_MAX : constant := 4800;
+
+ type R2_Range is new Natural range 0 .. LC_FREQ * 2 / REF_MIN;
+ type N2_Range is new Natural range 0 .. VCO_MAX * Natural (R2_Range'Last) / LC_FREQ;
+ type P_Range is new Natural range 0 .. P_MAX;
+
+ type RNP is record
+ P : P_Range;
+ N2 : N2_Range;
+ R2 : R2_Range;
+ end record;
+ Invalid_RNP : constant RNP := RNP'(0, 0, 0);
+
+ function Get_Budget_For_Freq
+ (Clock : HW.GFX.Frequency_Type)
+ return Word64
+ is
+ Result : Word64;
+ begin
+ case Clock is
+ when 25175000 |
+ 25200000 |
+ 27000000 |
+ 27027000 |
+ 37762500 |
+ 37800000 |
+ 40500000 |
+ 40541000 |
+ 54000000 |
+ 54054000 |
+ 59341000 |
+ 59400000 |
+ 72000000 |
+ 74176000 |
+ 74250000 |
+ 81000000 |
+ 81081000 |
+ 89012000 |
+ 89100000 |
+ 108000000 |
+ 108108000 |
+ 111264000 |
+ 111375000 |
+ 148352000 |
+ 148500000 |
+ 162000000 |
+ 162162000 |
+ 222525000 |
+ 222750000 |
+ 296703000 |
+ 297000000 =>
+ Result := 0;
+ when 233500000 |
+ 245250000 |
+ 247750000 |
+ 253250000 |
+ 298000000 =>
+ Result := 1500;
+ when 169128000 |
+ 169500000 |
+ 179500000 |
+ 202000000 =>
+ Result := 2000;
+ when 256250000 |
+ 262500000 |
+ 270000000 |
+ 272500000 |
+ 273750000 |
+ 280750000 |
+ 281250000 |
+ 286000000 |
+ 291750000 =>
+ Result := 4000;
+ when 267250000 |
+ 268500000 =>
+ Result := 5000;
+ when others =>
+ Result := 1000;
+ end case;
+ return Result;
+ end Get_Budget_For_Freq;
+
+ procedure Update_RNP
+ (Freq_2K : in Word64;
+ Budget : in Word64;
+ R2 : in R2_Range;
+ N2 : in N2_Range;
+ P : in P_Range;
+ Best : in out RNP)
+ with
+ Depends => (Best =>+ (Freq_2K, Budget, R2, N2, P))
+ is
+ use type HW.Word64;
+
+ function Abs_Diff (A, B : Word64) return Word64
+ is
+ Result : Word64;
+ begin
+ if A > B then
+ Result := A - B;
+ else
+ Result := B - A;
+ end if;
+ return Result;
+ end Abs_Diff;
+
+ A, B, C, D, Diff, Diff_Best : Word64;
+ begin
+ -- No best (r,n,p) yet */
+ if Best.P = 0 then
+ Best.P := P;
+ Best.N2 := N2;
+ Best.R2 := R2;
+ else
+ -- Config clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to
+ -- freq2k.
+ --
+ -- delta = 1e6 *
+ -- abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) /
+ -- freq2k;
+ --
+ -- and we would like delta <= budget.
+ --
+ -- If the discrepancy is above the PPM-based budget, always prefer to
+ -- improve upon the previous solution. However, if you're within the
+ -- budget, try to maximize Ref * VCO, that is N / (P * R^2).
+ A := Freq_2K * Budget * Word64 (P) * Word64 (R2);
+ B := Freq_2K * Budget * Word64 (Best.P) * Word64 (Best.R2);
+ Diff := Abs_Diff
+ (Freq_2K * Word64 (P) * Word64 (R2),
+ LC_FREQ_2K * Word64 (N2));
+ Diff_Best := Abs_Diff
+ (Freq_2K * Word64 (Best.P) * Word64 (Best.R2),
+ LC_FREQ_2K * Word64 (Best.N2));
+ C := 1000000 * Diff;
+ D := 1000000 * Diff_Best;
+
+ if A < C and B < D then
+ -- If both are above the Budget, pick the closer
+ if Word64 (Best.P) * Word64 (Best.R2) * Diff
+ < Word64 (P) * Word64 (R2) * Diff_Best
+ then
+ Best.P := P;
+ Best.N2 := N2;
+ Best.R2 := R2;
+ end if;
+ elsif A >= C and B < D then
+ -- If A is below the threshold but B is above it? Update.
+ Best.P := P;
+ Best.N2 := N2;
+ Best.R2 := R2;
+ elsif A >= C and B >= D then
+ -- Both are below the limit, so pick the higher N2/(R2*R2)
+ if Word64 (N2) * Word64 (Best.R2) * Word64 (Best.R2)
+ > Word64 (Best.N2) * Word64 (R2) * Word64 (R2)
+ then
+ Best.P := P;
+ Best.N2 := N2;
+ Best.R2 := R2;
+ end if;
+ end if;
+ -- Otherwise A < C && B >= D, do nothing
+ end if;
+ end Update_RNP;
+
+ procedure Calculate_WRPLL
+ (Clock : in HW.GFX.Frequency_Type;
+ R2_Out : out R2_Range;
+ N2_Out : out N2_Range;
+ P_Out : out P_Range)
+ with
+ Global => null,
+ Pre => True,
+ Post => True
+ is
+ use type HW.Word64;
+
+ Freq_2K : Word64;
+ Budget : Word64;
+ Best : RNP := Invalid_RNP;
+ begin
+ Freq_2K := Word64 (Clock) / 100; -- PLL output should be 5x
+ -- the pixel clock
+ Budget := Get_Budget_For_Freq (Clock);
+
+ -- Special case handling for 540MHz pixel clock: bypass WR PLL entirely
+ -- and directly pass the LC PLL to it. */
+ if Freq_2K = 5400000 then
+ N2_Out := 2;
+ P_Out := 1;
+ R2_Out := 2;
+ else
+ -- Ref = LC_FREQ / R, where Ref is the actual reference input seen by
+ -- the WR PLL.
+ --
+ -- We want R so that REF_MIN <= Ref <= REF_MAX.
+ -- Injecting R2 = 2 * R gives:
+ -- REF_MAX * r2 > LC_FREQ * 2 and
+ -- REF_MIN * r2 < LC_FREQ * 2
+ --
+ -- Which means the desired boundaries for r2 are:
+ -- LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN
+ --
+ for R2 in R2_Range range
+ LC_FREQ * 2 / REF_MAX + 1 .. LC_FREQ * 2 / REF_MIN
+ loop
+ -- VCO = N * Ref, that is: VCO = N * LC_FREQ / R
+ --
+ -- Once again we want VCO_MIN <= VCO <= VCO_MAX.
+ -- Injecting R2 = 2 * R and N2 = 2 * N, we get:
+ -- VCO_MAX * r2 > n2 * LC_FREQ and
+ -- VCO_MIN * r2 < n2 * LC_FREQ)
+ --
+ -- Which means the desired boundaries for n2 are:
+ -- VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ
+ for N2 in N2_Range range
+ N2_Range (VCO_MIN * Natural (R2) / LC_FREQ + 1)
+ .. N2_Range (VCO_MAX * Natural (R2) / LC_FREQ)
+ loop
+ for P_Fract in Natural range P_MIN / P_INC .. P_MAX / P_INC
+ loop
+ Update_RNP
+ (Freq_2K, Budget, R2, N2, P_Range (P_Fract * P_INC), Best);
+ end loop;
+ end loop;
+ end loop;
+
+ N2_Out := Best.N2;
+ P_Out := Best.P;
+ R2_Out := Best.R2;
+ end if;
+
+ end Calculate_WRPLL;
+
+ --
+ ----------------------------------------------------------------------------
+
+ type Regs is array (WRPLLs) of Registers.Registers_Index;
+
+ WRPLL_CTL : constant Regs := Regs'(Registers.WRPLL_CTL_1, Registers.WRPLL_CTL_2);
+ WRPLL_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
+ WRPLL_CTL_SELECT_LCPLL : constant := 3 * 2 ** 28;
+
+ function WRPLL_CTL_DIVIDER_FEEDBACK (N2 : N2_Range) return Word32
+ is
+ begin
+ return Word32 (N2) * 2 ** 16;
+ end WRPLL_CTL_DIVIDER_FEEDBACK;
+
+ function WRPLL_CTL_DIVIDER_POST (P : P_Range) return Word32
+ is
+ begin
+ return Word32 (P) * 2 ** 8;
+ end WRPLL_CTL_DIVIDER_POST;
+
+ function WRPLL_CTL_DIVIDER_REFERENCE (R2 : R2_Range) return Word32
+ is
+ begin
+ return Word32 (R2) * 2 ** 0;
+ end WRPLL_CTL_DIVIDER_REFERENCE;
+
+ ----------------------------------------------------------------------------
+
+ procedure On
+ (PLL : in WRPLLs;
+ Target_Clock : in Frequency_Type;
+ Success : out Boolean)
+ is
+ R2 : R2_Range;
+ N2 : N2_Range;
+ P : P_Range;
+ begin
+ Calculate_WRPLL (Target_Clock, R2, N2, P);
+ Registers.Write
+ (Register => WRPLL_CTL (PLL),
+ Value => WRPLL_CTL_PLL_ENABLE or
+ WRPLL_CTL_SELECT_LCPLL or
+ WRPLL_CTL_DIVIDER_FEEDBACK (N2) or
+ WRPLL_CTL_DIVIDER_POST (P) or
+ WRPLL_CTL_DIVIDER_REFERENCE (R2));
+ Registers.Posting_Read (WRPLL_CTL (PLL));
+ Time.U_Delay (20);
+
+ Success := True;
+ end On;
+
+ procedure Off (PLL : WRPLLs)
+ is
+ begin
+ Registers.Unset_Mask (WRPLL_CTL (PLL), WRPLL_CTL_PLL_ENABLE);
+ end Off;
+
+end HW.GFX.GMA.PLLs.WRPLL;
diff --git a/common/haswell/hw-gfx-gma-plls-wrpll.ads b/common/haswell/hw-gfx-gma-plls-wrpll.ads
new file mode 100644
index 0000000..48f6850
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls-wrpll.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs.WRPLL
+is
+
+ type Value_Array is array (WRPLLs) of Word32;
+ Register_Value : constant Value_Array := Value_Array'
+ (WRPLL0 => 4 * 2 ** 29, WRPLL1 => 5 * 2 ** 29);
+
+ procedure On
+ (PLL : in WRPLLs;
+ Target_Clock : in Frequency_Type;
+ Success : out Boolean);
+
+ procedure Off (PLL : WRPLLs);
+
+end HW.GFX.GMA.PLLs.WRPLL;
diff --git a/common/haswell/hw-gfx-gma-plls.adb b/common/haswell/hw-gfx-gma-plls.adb
new file mode 100644
index 0000000..45faa66
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls.adb
@@ -0,0 +1,130 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.PLLs.LCPLL;
+with HW.GFX.GMA.PLLs.WRPLL;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PLLs
+with
+ Refined_State => (State => PLLs)
+is
+
+ type Count_Range is new Natural range 0 .. 2;
+
+ type PLL_State is record
+ Use_Count : Count_Range;
+ Mode : Mode_Type;
+ end record;
+
+ type PLL_State_Array is array (WRPLLs) of PLL_State;
+
+ PLLs : PLL_State_Array;
+
+ procedure Initialize
+ is
+ begin
+ PLLs := (WRPLLs => (Use_Count => 0, Mode => Invalid_Mode));
+ end Initialize;
+
+ procedure Alloc_Configurable
+ (Mode : in Mode_Type;
+ PLL : out T;
+ Success : out Boolean)
+ with
+ Pre => True
+ is
+ begin
+ -- try to find shareable PLL
+ for P in WRPLLs loop
+ Success := PLLs (P).Use_Count /= 0 and
+ PLLs (P).Use_Count /= Count_Range'Last and
+ PLLs (P).Mode = Mode;
+ if Success then
+ PLL := P;
+ PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1;
+ return;
+ end if;
+ end loop;
+
+ -- try to find free PLL
+ for P in WRPLLs loop
+ if PLLs (P).Use_Count = 0 then
+ PLL := P;
+ WRPLL.On (PLL, Mode.Dotclock, Success);
+ if Success then
+ PLLs (PLL) := (Use_Count => 1, Mode => Mode);
+ end if;
+ return;
+ end if;
+ end loop;
+
+ PLL := Invalid;
+ end Alloc_Configurable;
+
+ procedure Alloc
+ (Port_Cfg : in Port_Config;
+ PLL : out T;
+ Success : out Boolean)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Port = DIGI_E then
+ PLL := Invalid;
+ Success := True;
+ elsif Port_Cfg.Display = DP then
+ PLL := LCPLL.Fixed_LCPLLs (Port_Cfg.DP.Bandwidth);
+ Success := True;
+ else
+ Alloc_Configurable (Port_Cfg.Mode, PLL, Success);
+ end if;
+ end Alloc;
+
+ procedure Free (PLL : T)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if PLL in WRPLLs then
+ if PLLs (PLL).Use_Count /= 0 then
+ PLLs (PLL).Use_Count := PLLs (PLL).Use_Count - 1;
+ if PLLs (PLL).Use_Count = 0 then
+ WRPLL.Off (PLL);
+ end if;
+ end if;
+ end if;
+ end Free;
+
+ procedure All_Off
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ for PLL in WRPLLs loop
+ WRPLL.Off (PLL);
+ end loop;
+ end All_Off;
+
+ function Register_Value (PLL : T) return Word32
+ is
+ begin
+ return
+ (if PLL in LCPLLs then LCPLL.Register_Value (PLL)
+ elsif PLL in WRPLLs then WRPLL.Register_Value (PLL)
+ else 0);
+ end Register_Value;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/haswell/hw-gfx-gma-plls.ads b/common/haswell/hw-gfx-gma-plls.ads
new file mode 100644
index 0000000..9b158ae
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-plls.ads
@@ -0,0 +1,40 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs
+with
+ Abstract_State => (State with Part_Of => GMA.State)
+is
+
+ -- XXX: Types should be private (but that triggers a bug in SPARK GPL 2016)
+ type T is (Invalid_PLL, LCPLL0, LCPLL1, LCPLL2, WRPLL0, WRPLL1);
+ subtype LCPLLs is T range LCPLL0 .. LCPLL2;
+ subtype WRPLLs is T range WRPLL0 .. WRPLL1;
+ Invalid : constant T := Invalid_PLL;
+
+ procedure Initialize
+ with
+ Global => (Output => State);
+
+ procedure Alloc
+ (Port_Cfg : in Port_Config;
+ PLL : out T;
+ Success : out Boolean);
+
+ procedure Free (PLL : T);
+
+ procedure All_Off;
+
+ function Register_Value (PLL : T) return Word32;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/haswell/hw-gfx-gma-power_and_clocks.ads b/common/haswell/hw-gfx-gma-power_and_clocks.ads
new file mode 100644
index 0000000..5e040ed
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-power_and_clocks.ads
@@ -0,0 +1,17 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Power_And_Clocks_Haswell;
+
+private package HW.GFX.GMA.Power_And_Clocks
+ renames HW.GFX.GMA.Power_And_Clocks_Haswell;
diff --git a/common/haswell/hw-gfx-gma-spll.adb b/common/haswell/hw-gfx-gma-spll.adb
new file mode 100644
index 0000000..71c9cd4
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-spll.adb
@@ -0,0 +1,43 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.SPLL is
+
+ SPLL_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
+ SPLL_CTL_REF_SEL_MASK : constant := 3 * 2 ** 28;
+ SPLL_CTL_REF_SEL_SSC : constant := 1 * 2 ** 28;
+ SPLL_CTL_REF_SEL_NON_SSC : constant := 2 * 2 ** 28;
+ SPLL_CTL_FREQ_SEL_MASK : constant := 3 * 2 ** 26;
+ SPLL_CTL_FREQ_SEL_810 : constant := 0 * 2 ** 26;
+ SPLL_CTL_FREQ_SEL_1350 : constant := 1 * 2 ** 26;
+
+ procedure On is
+ begin
+ Registers.Write
+ (Register => Registers.SPLL_CTL,
+ Value => SPLL_CTL_PLL_ENABLE or
+ SPLL_CTL_REF_SEL_SSC or
+ SPLL_CTL_FREQ_SEL_1350);
+ Registers.Posting_Read (Registers.SPLL_CTL);
+ Time.U_Delay (20);
+ end On;
+
+ procedure Off is
+ begin
+ Registers.Unset_Mask (Registers.SPLL_CTL, SPLL_CTL_PLL_ENABLE);
+ end Off;
+
+end HW.GFX.GMA.SPLL;
diff --git a/common/haswell/hw-gfx-gma-spll.ads b/common/haswell/hw-gfx-gma-spll.ads
new file mode 100644
index 0000000..223f856
--- /dev/null
+++ b/common/haswell/hw-gfx-gma-spll.ads
@@ -0,0 +1,20 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.SPLL is
+
+ procedure On;
+
+ procedure Off;
+
+end HW.GFX.GMA.SPLL;
diff --git a/common/haswell_shared/Makefile.inc b/common/haswell_shared/Makefile.inc
new file mode 100644
index 0000000..45b505b
--- /dev/null
+++ b/common/haswell_shared/Makefile.inc
@@ -0,0 +1,6 @@
+gfxinit-y += hw-gfx-gma-connectors-ddi.adb
+gfxinit-y += hw-gfx-gma-connectors-ddi.ads
+gfxinit-y += hw-gfx-gma-connectors.adb
+gfxinit-y += hw-gfx-gma-port_detect.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_haswell.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_haswell.ads
diff --git a/common/haswell_shared/hw-gfx-gma-connectors-ddi.adb b/common/haswell_shared/hw-gfx-gma-connectors-ddi.adb
new file mode 100644
index 0000000..0459681
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-connectors-ddi.adb
@@ -0,0 +1,554 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.DP_Training;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.PCH.FDI;
+with HW.GFX.GMA.PCH.Transcoder;
+with HW.GFX.GMA.PCH.VGA;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.SPLL;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.DDI is
+
+ DDI_BUF_CTL_BUFFER_ENABLE : constant := 1 * 2 ** 31;
+ DDI_BUF_CTL_TRANS_SELECT_MASK : constant := 15 * 2 ** 24;
+ DDI_BUF_CTL_PORT_REVERSAL : constant := 1 * 2 ** 16;
+ DDI_BUF_CTL_IDLE_STATUS : constant := 1 * 2 ** 7;
+ DDI_BUF_CTL_DDI_A_LANE_CAP : constant := 1 * 2 ** 4;
+ DDI_BUF_CTL_PORT_WIDTH_MASK : constant := 7 * 2 ** 1;
+ DDI_BUF_CTL_PORT_WIDTH_1_LANE : constant := 0 * 2 ** 1;
+ DDI_BUF_CTL_PORT_WIDTH_2_LANES : constant := 1 * 2 ** 1;
+ DDI_BUF_CTL_PORT_WIDTH_4_LANES : constant := 3 * 2 ** 1;
+ DDI_BUF_CTL_INIT_DISPLAY_DETECT : constant := 1 * 2 ** 0;
+
+ subtype DDI_BUF_CTL_TRANS_SELECT_T is Natural range 0 .. 9;
+ function DDI_BUF_CTL_TRANS_SELECT
+ (Sel : DDI_BUF_CTL_TRANS_SELECT_T)
+ return Word32;
+
+ type DDI_BUF_CTL_PORT_WIDTH_T is array (HW.GFX.DP_Lane_Count) of Word32;
+ DDI_BUF_CTL_PORT_WIDTH : constant DDI_BUF_CTL_PORT_WIDTH_T :=
+ DDI_BUF_CTL_PORT_WIDTH_T'
+ (HW.GFX.DP_Lane_Count_1 => DDI_BUF_CTL_PORT_WIDTH_1_LANE,
+ HW.GFX.DP_Lane_Count_2 => DDI_BUF_CTL_PORT_WIDTH_2_LANES,
+ HW.GFX.DP_Lane_Count_4 => DDI_BUF_CTL_PORT_WIDTH_4_LANES);
+
+ DP_TP_CTL_TRANSPORT_ENABLE : constant := 1 * 2 ** 31;
+ DP_TP_CTL_MODE_SST : constant := 0 * 2 ** 27;
+ DP_TP_CTL_MODE_MST : constant := 1 * 2 ** 27;
+ DP_TP_CTL_FORCE_ACT : constant := 1 * 2 ** 25;
+ DP_TP_CTL_ENHANCED_FRAME_ENABLE : constant := 1 * 2 ** 18;
+ DP_TP_CTL_FDI_AUTOTRAIN : constant := 1 * 2 ** 15;
+ DP_TP_CTL_LINK_TRAIN_MASK : constant := 7 * 2 ** 8;
+ DP_TP_CTL_LINK_TRAIN_PAT1 : constant := 0 * 2 ** 8;
+ DP_TP_CTL_LINK_TRAIN_PAT2 : constant := 1 * 2 ** 8;
+ DP_TP_CTL_LINK_TRAIN_PAT3 : constant := 4 * 2 ** 8;
+ DP_TP_CTL_LINK_TRAIN_IDLE : constant := 2 * 2 ** 8;
+ DP_TP_CTL_LINK_TRAIN_NORMAL : constant := 3 * 2 ** 8;
+ DP_TP_CTL_SCRAMBLE_DISABLE : constant := 1 * 2 ** 7;
+ DP_TP_CTL_ALT_SCRAMBLER_RESET : constant := 1 * 2 ** 6;
+
+ type DP_TP_CTL_LINK_TRAIN_Array is
+ array (DP_Info.Training_Pattern) of Word32;
+ DP_TP_CTL_LINK_TRAIN : constant DP_TP_CTL_LINK_TRAIN_Array :=
+ DP_TP_CTL_LINK_TRAIN_Array'
+ (DP_Info.TP_1 => DP_TP_CTL_LINK_TRAIN_PAT1 or DP_TP_CTL_SCRAMBLE_DISABLE,
+ DP_Info.TP_2 => DP_TP_CTL_LINK_TRAIN_PAT2 or DP_TP_CTL_SCRAMBLE_DISABLE,
+ DP_Info.TP_3 => DP_TP_CTL_LINK_TRAIN_PAT3 or DP_TP_CTL_SCRAMBLE_DISABLE,
+ DP_Info.TP_Idle => DP_TP_CTL_LINK_TRAIN_IDLE,
+ DP_Info.TP_None => DP_TP_CTL_LINK_TRAIN_NORMAL);
+
+ DP_TP_STATUS_MIN_IDLES_SENT : constant := 1 * 2 ** 25;
+ DP_TP_STATUS_FDI_AUTO_TRAIN_DONE : constant := 1 * 2 ** 12;
+
+ PORT_CLK_SEL_LCPLL2700 : constant := 0 * 2 ** 29; -- not on ULX
+ PORT_CLK_SEL_LCPLL1350 : constant := 1 * 2 ** 29;
+ PORT_CLK_SEL_LCPLL810 : constant := 2 * 2 ** 29;
+ PORT_CLK_SEL_SPLL : constant := 3 * 2 ** 29;
+ PORT_CLK_SEL_WRPLL1 : constant := 4 * 2 ** 29;
+ PORT_CLK_SEL_WRPLL2 : constant := 5 * 2 ** 29;
+ PORT_CLK_SEL_NONE : constant := 7 * 2 ** 29;
+
+ type PORT_CLK_SEL_LCPLL_T is array (HW.GFX.DP_Bandwidth) of Word32;
+ PORT_CLK_SEL_LCPLL : constant PORT_CLK_SEL_LCPLL_T :=
+ PORT_CLK_SEL_LCPLL_T'
+ (HW.GFX.DP_Bandwidth_1_62 => PORT_CLK_SEL_LCPLL810,
+ HW.GFX.DP_Bandwidth_2_7 => PORT_CLK_SEL_LCPLL1350,
+ HW.GFX.DP_Bandwidth_5_4 => PORT_CLK_SEL_LCPLL2700);
+
+ type DDI_Registers is record
+ BUF_CTL : Registers.Registers_Index;
+ DP_TP_CTL : Registers.Registers_Index;
+ DP_TP_STATUS : Registers.Registers_Invalid_Index;
+ PORT_CLK_SEL : Registers.Registers_Index;
+ end record;
+
+ type DDI_Registers_Array is array (Digital_Port) of DDI_Registers;
+
+ DDI_Regs : constant DDI_Registers_Array := DDI_Registers_Array'
+ (DIGI_A => DDI_Registers'
+ (BUF_CTL => Registers.DDI_BUF_CTL_A,
+ DP_TP_CTL => Registers.DP_TP_CTL_A,
+ DP_TP_STATUS => Registers.Invalid_Register,
+ PORT_CLK_SEL => Registers.PORT_CLK_SEL_DDIA),
+ DIGI_B => DDI_Registers'
+ (BUF_CTL => Registers.DDI_BUF_CTL_B,
+ DP_TP_CTL => Registers.DP_TP_CTL_B,
+ DP_TP_STATUS => Registers.DP_TP_STATUS_B,
+ PORT_CLK_SEL => Registers.PORT_CLK_SEL_DDIB),
+ DIGI_C => DDI_Registers'
+ (BUF_CTL => Registers.DDI_BUF_CTL_C,
+ DP_TP_CTL => Registers.DP_TP_CTL_C,
+ DP_TP_STATUS => Registers.DP_TP_STATUS_C,
+ PORT_CLK_SEL => Registers.PORT_CLK_SEL_DDIC),
+ DIGI_D => DDI_Registers'
+ (BUF_CTL => Registers.DDI_BUF_CTL_D,
+ DP_TP_CTL => Registers.DP_TP_CTL_D,
+ DP_TP_STATUS => Registers.DP_TP_STATUS_D,
+ PORT_CLK_SEL => Registers.PORT_CLK_SEL_DDID),
+ DIGI_E => DDI_Registers'
+ (BUF_CTL => Registers.DDI_BUF_CTL_E,
+ DP_TP_CTL => Registers.DP_TP_CTL_E,
+ DP_TP_STATUS => Registers.DP_TP_STATUS_E,
+ PORT_CLK_SEL => Registers.PORT_CLK_SEL_DDIE));
+
+ ----------------------------------------------------------------------------
+
+ type Values is array (Digital_Port) of Word32;
+ type Shifts is array (Digital_Port) of Natural;
+
+ DPLL_CTRL2_DDIx_CLOCK_OFF : constant Values := Values'
+ (DIGI_A => 1 * 2 ** 15,
+ DIGI_B => 1 * 2 ** 16,
+ DIGI_C => 1 * 2 ** 17,
+ DIGI_D => 1 * 2 ** 18,
+ DIGI_E => 1 * 2 ** 19);
+
+ DPLL_CTRL2_DDIx_SELECT_MASK : constant Values := Values'
+ (DIGI_A => 3 * 2 ** 1,
+ DIGI_B => 3 * 2 ** 4,
+ DIGI_C => 3 * 2 ** 7,
+ DIGI_D => 3 * 2 ** 10,
+ DIGI_E => 3 * 2 ** 13);
+ DPLL_CTRL2_DDIx_SELECT_SHIFT : constant Shifts := Shifts'
+ (DIGI_A => 1,
+ DIGI_B => 4,
+ DIGI_C => 7,
+ DIGI_D => 10,
+ DIGI_E => 13);
+
+ DPLL_CTRL2_DDIx_SELECT_OVERRIDE : constant Values := Values'
+ (DIGI_A => 1 * 2 ** 0,
+ DIGI_B => 1 * 2 ** 3,
+ DIGI_C => 1 * 2 ** 6,
+ DIGI_D => 1 * 2 ** 9,
+ DIGI_E => 1 * 2 ** 12);
+
+ ----------------------------------------------------------------------------
+
+ function DDI_BUF_CTL_TRANS_SELECT
+ (Sel : DDI_BUF_CTL_TRANS_SELECT_T)
+ return Word32
+ is
+ begin
+ return Word32 (Sel) * 2 ** 24;
+ end DDI_BUF_CTL_TRANS_SELECT;
+
+ ----------------------------------------------------------------------------
+
+ function Max_V_Swing
+ (Port : Digital_Port)
+ return DP_Info.DP_Voltage_Swing
+ is
+ begin
+ return
+ (if (Config.Has_Low_Voltage_Swing and Config.EDP_Low_Voltage_Swing)
+ and then Port = DIGI_A
+ then
+ DP_Info.VS_Level_3
+ else
+ DP_Info.VS_Level_2);
+ end Max_V_Swing;
+
+ pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+ Reason => "Needed for a common interface");
+ function Max_Pre_Emph
+ (Port : Digital_Port;
+ Train_Set : DP_Info.Train_Set)
+ return DP_Info.DP_Pre_Emph
+ is
+ begin
+ return
+ (case Train_Set.Voltage_Swing is
+ when DP_Info.VS_Level_0 => DP_Info.Emph_Level_3,
+ when DP_Info.VS_Level_1 => DP_Info.Emph_Level_2,
+ when DP_Info.VS_Level_2 => DP_Info.Emph_Level_1,
+ when others => DP_Info.Emph_Level_0);
+ end Max_Pre_Emph;
+ pragma Warnings (GNATprove, On, "unused variable ""Port""");
+
+ ----------------------------------------------------------------------------
+
+ procedure Set_TP_CTL
+ (Port : Digital_Port;
+ Link : DP_Link;
+ Pattern : DP_Info.Training_Pattern)
+ is
+ DP_TP_CTL_Enhanced_Frame : Word32 := 0;
+ begin
+ if Link.Enhanced_Framing then
+ DP_TP_CTL_Enhanced_Frame := DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+ end if;
+
+ Registers.Write
+ (Register => DDI_Regs (Port).DP_TP_CTL,
+ Value => DP_TP_CTL_TRANSPORT_ENABLE or
+ DP_TP_CTL_Enhanced_Frame or
+ DP_TP_CTL_LINK_TRAIN (Pattern));
+ end Set_TP_CTL;
+
+ procedure Set_Training_Pattern
+ (Port : Digital_Port;
+ Link : DP_Link;
+ Pattern : DP_Info.Training_Pattern)
+ is
+ use type DP_Info.Training_Pattern;
+ begin
+ if Pattern < DP_Info.TP_Idle then
+ Set_TP_CTL (Port, Link, Pattern);
+ else
+ -- send at least 5 idle patterns
+ Set_TP_CTL (Port, Link, DP_Info.TP_Idle);
+
+ -- switch to normal frame delivery
+ if Config.End_EDP_Training_Late and then Port = DIGI_A then
+ null; -- do it later in Post_On procedure
+ -- TODO: if there are problems getting the pipe up,
+ -- wait here some time
+ -- Time.U_Delay (100);
+ else
+ if Port /= DIGI_A then
+ Registers.Wait_Set_Mask
+ (Register => DDI_Regs (Port).DP_TP_STATUS,
+ Mask => DP_TP_STATUS_MIN_IDLES_SENT);
+ end if;
+ Set_TP_CTL (Port, Link, DP_Info.TP_None);
+ end if;
+ end if;
+ end Set_Training_Pattern;
+
+ procedure Set_Signal_Levels
+ (Port : Digital_Port;
+ Link : DP_Link;
+ Train_Set : DP_Info.Train_Set)
+ is
+ Was_Enabled : Boolean;
+ Trans_Select : DDI_BUF_CTL_TRANS_SELECT_T;
+ begin
+ case Train_Set.Voltage_Swing is
+ when DP_Info.VS_Level_0 =>
+ case Train_Set.Pre_Emph is
+ when DP_Info.Emph_Level_0 => Trans_Select := 0;
+ when DP_Info.Emph_Level_1 => Trans_Select := 1;
+ when DP_Info.Emph_Level_2 => Trans_Select := 2;
+ when DP_Info.Emph_Level_3 => Trans_Select := 3;
+ end case;
+ when DP_Info.VS_Level_1 =>
+ case Train_Set.Pre_Emph is
+ when DP_Info.Emph_Level_0 => Trans_Select := 4;
+ when DP_Info.Emph_Level_1 => Trans_Select := 5;
+ when DP_Info.Emph_Level_2 => Trans_Select := 6;
+ when others => Trans_Select := 0;
+ end case;
+ when DP_Info.VS_Level_2 =>
+ case Train_Set.Pre_Emph is
+ when DP_Info.Emph_Level_0 => Trans_Select := 7;
+ when DP_Info.Emph_Level_1 => Trans_Select := 8;
+ when others => Trans_Select := 0;
+ end case;
+ when DP_Info.VS_Level_3 =>
+ case Train_Set.Pre_Emph is
+ when DP_Info.Emph_Level_0 => Trans_Select := 9;
+ when others => Trans_Select := 0;
+ end case;
+ end case;
+
+ Registers.Is_Set_Mask
+ (Register => DDI_Regs (Port).BUF_CTL,
+ Mask => DDI_BUF_CTL_BUFFER_ENABLE,
+ Result => Was_Enabled);
+
+ -- enable DDI buffer
+ Registers.Unset_And_Set_Mask
+ (Register => DDI_Regs (Port).BUF_CTL,
+ Mask_Unset => DDI_BUF_CTL_TRANS_SELECT_MASK or
+ DDI_BUF_CTL_PORT_REVERSAL or
+ DDI_BUF_CTL_PORT_WIDTH_MASK,
+ Mask_Set => DDI_BUF_CTL_BUFFER_ENABLE or
+ DDI_BUF_CTL_TRANS_SELECT (Trans_Select) or
+ DDI_BUF_CTL_PORT_WIDTH (Link.Lane_Count));
+ Registers.Posting_Read (DDI_Regs (Port).BUF_CTL);
+
+ if not Was_Enabled then
+ Time.U_Delay (600); -- wait >= 518us (intel spec)
+ end if;
+ end Set_Signal_Levels;
+
+ ----------------------------------------------------------------------------
+
+ procedure Digital_Off (Port : Digital_Port)
+ is
+ Enabled : Boolean;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Is_Set_Mask
+ (Register => DDI_Regs (Port).BUF_CTL,
+ Mask => DDI_BUF_CTL_BUFFER_ENABLE,
+ Result => Enabled);
+
+ if Enabled then
+ Registers.Unset_Mask
+ (Register => DDI_Regs (Port).BUF_CTL,
+ Mask => DDI_BUF_CTL_BUFFER_ENABLE);
+ end if;
+
+ Registers.Unset_Mask
+ (Register => DDI_Regs (Port).DP_TP_CTL,
+ Mask => DP_TP_CTL_TRANSPORT_ENABLE);
+
+ if Enabled then
+ Registers.Wait_Set_Mask
+ (Register => DDI_Regs (Port).BUF_CTL,
+ Mask => DDI_BUF_CTL_IDLE_STATUS);
+ end if;
+
+ if Config.Has_Per_DDI_Clock_Sel then
+ Registers.Write
+ (Register => DDI_Regs (Port).PORT_CLK_SEL,
+ Value => PORT_CLK_SEL_NONE);
+ else
+ Registers.Set_Mask
+ (Register => Registers.DPLL_CTRL2,
+ Mask => DPLL_CTRL2_DDIx_CLOCK_OFF (Port));
+ end if;
+ end Digital_Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure Train_FDI
+ (Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ is
+ begin
+ PCH.FDI.Pre_Train (PCH.FDI_A, Port_Cfg);
+
+ -- always use SPLL for FDI
+ SPLL.On;
+ Registers.Write
+ (Register => DDI_Regs (DIGI_E).PORT_CLK_SEL,
+ Value => PORT_CLK_SEL_SPLL);
+
+ -- try each preemph/voltage pair twice
+ for Trans2 in Natural range 0 .. DDI_BUF_CTL_TRANS_SELECT_T'Last * 2 + 1
+ loop
+ Registers.Write
+ (Register => DDI_Regs (DIGI_E).DP_TP_CTL,
+ Value => DP_TP_CTL_TRANSPORT_ENABLE or
+ DP_TP_CTL_ENHANCED_FRAME_ENABLE or
+ DP_TP_CTL_FDI_AUTOTRAIN or
+ DP_TP_CTL_LINK_TRAIN_PAT1);
+
+ Registers.Unset_And_Set_Mask
+ (Register => DDI_Regs (DIGI_E).BUF_CTL,
+ Mask_Unset => DDI_BUF_CTL_TRANS_SELECT_MASK or
+ DDI_BUF_CTL_PORT_REVERSAL or
+ DDI_BUF_CTL_PORT_WIDTH_MASK,
+ Mask_Set => DDI_BUF_CTL_BUFFER_ENABLE or
+ DDI_BUF_CTL_TRANS_SELECT (Trans2 / 2) or
+ DDI_BUF_CTL_PORT_WIDTH (Port_Cfg.FDI.Lane_Count));
+ Registers.Posting_Read (DDI_Regs (DIGI_E).BUF_CTL);
+ Time.U_Delay (600); -- wait >= 518us (intel spec)
+
+ PCH.FDI.Auto_Train (PCH.FDI_A);
+ Registers.Is_Set_Mask
+ (Register => DDI_Regs (DIGI_E).DP_TP_STATUS,
+ Mask => DP_TP_STATUS_FDI_AUTO_TRAIN_DONE,
+ Result => Success);
+ exit when Success;
+
+ Registers.Unset_Mask
+ (Register => DDI_Regs (DIGI_E).BUF_CTL,
+ Mask => DDI_BUF_CTL_BUFFER_ENABLE);
+ Registers.Posting_Read (DDI_Regs (DIGI_E).BUF_CTL);
+
+ Registers.Unset_And_Set_Mask
+ (Register => DDI_Regs (DIGI_E).DP_TP_CTL,
+ Mask_Unset => DP_TP_CTL_TRANSPORT_ENABLE or
+ DP_TP_CTL_LINK_TRAIN_MASK,
+ Mask_Set => DP_TP_CTL_LINK_TRAIN_PAT1);
+ Registers.Posting_Read (DDI_Regs (DIGI_E).DP_TP_CTL);
+
+ Registers.Wait_Set_Mask
+ (Register => DDI_Regs (DIGI_E).BUF_CTL,
+ Mask => DDI_BUF_CTL_IDLE_STATUS);
+
+ PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Lanes_Off);
+ end loop;
+
+ if Success then
+ -- start normal frame delivery
+ Registers.Write
+ (Register => DDI_Regs (DIGI_E).DP_TP_CTL,
+ Value => DP_TP_CTL_TRANSPORT_ENABLE or
+ DP_TP_CTL_ENHANCED_FRAME_ENABLE or
+ DP_TP_CTL_FDI_AUTOTRAIN or
+ DP_TP_CTL_LINK_TRAIN_NORMAL);
+ else
+ Registers.Write
+ (Register => DDI_Regs (DIGI_E).PORT_CLK_SEL,
+ Value => PORT_CLK_SEL_NONE);
+ SPLL.Off;
+
+ PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Clock_Off);
+ end if;
+ end Train_FDI;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_On
+ (Port_Cfg : in Port_Config;
+ PLL_Hint : in Word32;
+ Success : out Boolean)
+ is
+ function To_DP (Port : Digital_Port) return DP_Port
+ is
+ begin
+ return
+ (case Port is
+ when DIGI_A => DP_A,
+ when DIGI_B => DP_B,
+ when DIGI_C => DP_C,
+ when DIGI_D => DP_D,
+ when others => DP_Port'First);
+ end To_DP;
+ package Training is new DP_Training
+ (TPS3_Supported => True,
+ T => Digital_Port,
+ Aux_T => DP_Port,
+ Aux_Ch => DP_Aux_Ch,
+ DP_Info => DP_Info,
+ To_Aux => To_DP,
+ Max_V_Swing => Max_V_Swing,
+ Max_Pre_Emph => Max_Pre_Emph,
+ Set_Pattern => Set_Training_Pattern,
+ Set_Signal_Levels => Set_Signal_Levels,
+ Off => Digital_Off);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Display = VGA then
+ Train_FDI (Port_Cfg, Success);
+ else
+ -- direct configured PLL output to this port
+ if Config.Has_Per_DDI_Clock_Sel then
+ Registers.Write
+ (Register => DDI_Regs (Port_Cfg.Port).PORT_CLK_SEL,
+ Value => PLL_Hint);
+ else
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.DPLL_CTRL2,
+ Mask_Unset => DPLL_CTRL2_DDIx_CLOCK_OFF (Port_Cfg.Port) or
+ DPLL_CTRL2_DDIx_SELECT_MASK (Port_Cfg.Port),
+ Mask_Set => Shift_Left
+ (PLL_Hint,
+ DPLL_CTRL2_DDIx_SELECT_SHIFT (Port_Cfg.Port))
+ or
+ DPLL_CTRL2_DDIx_SELECT_OVERRIDE (Port_Cfg.Port));
+ end if;
+
+ if Port_Cfg.Display = DP then
+ Training.Train_DP
+ (Port => Port_Cfg.Port,
+ Link => Port_Cfg.DP,
+ Success => Success);
+ else
+ Success := True;
+ end if;
+ end if;
+ end Pre_On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Post_On (Port_Cfg : Port_Config)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Port = DIGI_A then
+ if Config.End_EDP_Training_Late then
+ Registers.Unset_And_Set_Mask
+ (Register => DDI_Regs (DIGI_A).DP_TP_CTL,
+ Mask_Unset => DP_TP_CTL_LINK_TRAIN_MASK,
+ Mask_Set => DP_TP_CTL_LINK_TRAIN_NORMAL);
+ end if;
+ end if;
+
+ case Port_Cfg.Display is
+ when HDMI =>
+ Registers.Unset_And_Set_Mask
+ (Register => DDI_Regs (Port_Cfg.Port).BUF_CTL,
+ Mask_Unset => DDI_BUF_CTL_TRANS_SELECT_MASK or
+ DDI_BUF_CTL_PORT_REVERSAL,
+ Mask_Set => DDI_BUF_CTL_BUFFER_ENABLE);
+ Time.U_Delay (600); -- wait >= 518us (intel spec)
+ when VGA =>
+ PCH.VGA.Clock_On (Port_Cfg.Mode);
+ PCH.Transcoder.On (Port_Cfg, PCH.FDI_A, 0);
+ PCH.VGA.On
+ (Port => PCH.FDI_A,
+ Mode => Port_Cfg.Mode);
+ when others =>
+ null;
+ end case;
+ end Post_On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Off (Port : Digital_Port)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port = DIGI_E then
+ PCH.VGA.Off;
+ PCH.Transcoder.Off (PCH.FDI_A);
+ -- PCH.VGA.Clock_Off; -- Can't tell what Linux does, if anything.
+ PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Rx_Off);
+ end if;
+
+ Digital_Off (Port);
+
+ if Port = DIGI_E then
+ SPLL.Off;
+ PCH.FDI.Off (PCH.FDI_A, PCH.FDI.Clock_Off);
+ end if;
+ end Off;
+
+end HW.GFX.GMA.Connectors.DDI;
diff --git a/common/haswell_shared/hw-gfx-gma-connectors-ddi.ads b/common/haswell_shared/hw-gfx-gma-connectors-ddi.ads
new file mode 100644
index 0000000..337e77b
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-connectors-ddi.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2015 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Registers;
+
+private package HW.GFX.GMA.Connectors.DDI
+is
+
+ procedure Pre_On
+ (Port_Cfg : in Port_Config;
+ PLL_Hint : in Word32;
+ Success : out Boolean);
+
+ procedure Post_On (Port_Cfg : Port_Config);
+
+ procedure Off (Port : Digital_Port);
+
+end HW.GFX.GMA.Connectors.DDI;
diff --git a/common/haswell_shared/hw-gfx-gma-connectors.adb b/common/haswell_shared/hw-gfx-gma-connectors.adb
new file mode 100644
index 0000000..e53902b
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-connectors.adb
@@ -0,0 +1,93 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Config;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.Connectors.DDI;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors is
+
+ procedure Pre_On
+ (Port_Cfg : in Port_Config;
+ PLL_Hint : in Word32;
+ Pipe_Hint : in Word32;
+ Success : out Boolean)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ DDI.Pre_On (Port_Cfg, PLL_Hint, Success);
+ end Pre_On;
+
+ procedure Post_On
+ (Port_Cfg : in Port_Config;
+ PLL_Hint : in Word32;
+ Success : out Boolean)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ DDI.Post_On (Port_Cfg);
+
+ if Port_Cfg.Port = DIGI_A then
+ Panel.Backlight_On;
+ end if;
+
+ Success := True;
+ end Post_On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_Off (Port_Cfg : Port_Config)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Port = DIGI_A then
+ Panel.Backlight_Off;
+ Panel.Off;
+ end if;
+ end Pre_Off;
+
+ procedure Post_Off (Port_Cfg : Port_Config)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ DDI.Off (Port_Cfg.Port);
+ end Post_Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_All_Off
+ is
+ begin
+ Panel.Backlight_Off;
+ Panel.Off;
+ end Pre_All_Off;
+
+ procedure Post_All_Off
+ is
+ begin
+ for Port in Digital_Port range DIGI_A .. DIGI_D loop
+ DDI.Off (Port);
+ end loop;
+ if Config.FDI_Port (DIGI_E) then
+ DDI.Off (DIGI_E);
+ end if;
+ end Post_All_Off;
+
+end HW.GFX.GMA.Connectors;
diff --git a/common/haswell_shared/hw-gfx-gma-port_detect.adb b/common/haswell_shared/hw-gfx-gma-port_detect.adb
new file mode 100644
index 0000000..0becd2c
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-port_detect.adb
@@ -0,0 +1,189 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Port_Detect
+is
+
+ PCH_ADPA_CRT_HPD_CHANNEL_MASK : constant := 3 * 2 ** 24;
+ PCH_ADPA_CRT_HPD_ENABLE : constant := 1 * 2 ** 23;
+
+ SFUSE_STRAP_CRT_DAC_CAP_DISABLE : constant := 1 * 2 ** 6;
+
+ HOTPLUG_CTL_DDI_A_HPD_INPUT_ENABLE : constant := 1 * 2 ** 4;
+ HOTPLUG_CTL_DDI_A_HPD_STATUS : constant := 3 * 2 ** 0;
+ HOTPLUG_CTL_DDI_A_HPD_LONG_DETECT : constant := 1 * 2 ** 1;
+
+ SHOTPLUG_CTL_DETECT_MASK : constant := 16#0303_0303#;
+
+ type Digital_Port_Value is array (Digital_Port) of Word32;
+ DDI_PORT_DETECTED : constant Digital_Port_Value :=
+ (DIGI_B => 1 * 2 ** 2,
+ DIGI_C => 1 * 2 ** 1,
+ DIGI_D => 1 * 2 ** 0,
+ DIGI_A => 1 * 2 ** 0,
+ others => 0);
+ SHOTPLUG_CTL_HPD_INPUT_ENABLE : constant Digital_Port_Value :=
+ (DIGI_B => 1 * 2 ** 4,
+ DIGI_C => 1 * 2 ** 12,
+ DIGI_D => 1 * 2 ** 20,
+ DIGI_A => 1 * 2 ** 28,
+ others => 0);
+ SHOTPLUG_CTL_HPD_STATUS : constant Digital_Port_Value :=
+ (DIGI_B => 3 * 2 ** 0,
+ DIGI_C => 3 * 2 ** 8,
+ DIGI_D => 3 * 2 ** 16,
+ DIGI_A => 3 * 2 ** 24,
+ others => 0);
+ SHOTPLUG_CTL_LONG_DETECT : constant Digital_Port_Value :=
+ (DIGI_B => 1 * 2 ** 1,
+ DIGI_C => 1 * 2 ** 9,
+ DIGI_D => 1 * 2 ** 17,
+ DIGI_A => 1 * 2 ** 25,
+ others => 0);
+
+ procedure Initialize
+ is
+ DAC_Disabled,
+ Internal_Detected,
+ DDI_Detected : Boolean;
+
+ Last_Digital_Port : constant Digital_Port :=
+ (if Config.Has_DDI_D then DIGI_D else DIGI_C);
+
+ subtype Ext_Digital_Port is
+ Digital_Port range DIGI_B .. DIGI_D;
+ type Digital_Port_To_GMA_Port is array (Ext_Digital_Port) of Port_Type;
+ To_HDMI_Port : constant Digital_Port_To_GMA_Port :=
+ (DIGI_B => Digital1,
+ DIGI_C => Digital2,
+ DIGI_D => Digital3);
+ To_DP_Port : constant Digital_Port_To_GMA_Port :=
+ (DIGI_B => DP1,
+ DIGI_C => DP2,
+ DIGI_D => DP3);
+ begin
+ if Config.Has_PCH_DAC then
+ -- PCH_DAC (_A)
+ Registers.Is_Set_Mask
+ (Register => Registers.SFUSE_STRAP,
+ Mask => SFUSE_STRAP_CRT_DAC_CAP_DISABLE,
+ Result => DAC_Disabled);
+ if not DAC_Disabled then
+ Registers.Set_Mask
+ (Register => Registers.PCH_ADPA,
+ Mask => PCH_ADPA_CRT_HPD_CHANNEL_MASK or -- clear status
+ PCH_ADPA_CRT_HPD_ENABLE);
+ end if;
+ Config.Valid_Port (Analog) := not DAC_Disabled;
+ end if;
+
+ if Config.Internal_Is_EDP then
+ -- DDI_A
+ Registers.Is_Set_Mask
+ (Register => Registers.DDI_BUF_CTL_A,
+ Mask => DDI_PORT_DETECTED (DIGI_A),
+ Result => Internal_Detected);
+ if Internal_Detected then
+ if Config.Has_HOTPLUG_CTL then
+ Registers.Set_Mask
+ (Register => Registers.HOTPLUG_CTL,
+ Mask => HOTPLUG_CTL_DDI_A_HPD_INPUT_ENABLE or
+ HOTPLUG_CTL_DDI_A_HPD_STATUS); -- clear status
+ if Config.Has_SHOTPLUG_CTL_A then
+ -- Have to enable south hotplug too on SoCs.
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.SHOTPLUG_CTL,
+ Mask_Unset => SHOTPLUG_CTL_DETECT_MASK,
+ Mask_Set => SHOTPLUG_CTL_HPD_INPUT_ENABLE (DIGI_A));
+ end if;
+ else
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.SHOTPLUG_CTL,
+ Mask_Unset => SHOTPLUG_CTL_DETECT_MASK,
+ Mask_Set => SHOTPLUG_CTL_HPD_INPUT_ENABLE (DIGI_A) or
+ SHOTPLUG_CTL_HPD_STATUS (DIGI_A)); -- clear
+ end if;
+ end if;
+ else
+ Internal_Detected := False;
+ end if;
+ Config.Valid_Port (Internal) := Internal_Detected;
+
+ -- DDI_[BCD]
+ for Port in Ext_Digital_Port range DIGI_B .. Last_Digital_Port loop
+ Registers.Is_Set_Mask
+ (Register => Registers.SFUSE_STRAP,
+ Mask => DDI_PORT_DETECTED (Port),
+ Result => DDI_Detected);
+ Config.Valid_Port (To_HDMI_Port (Port)) :=
+ Config.Valid_Port (To_HDMI_Port (Port)) and DDI_Detected;
+ Config.Valid_Port (To_DP_Port (Port)) :=
+ Config.Valid_Port (To_DP_Port (Port)) and DDI_Detected;
+
+ if DDI_Detected then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.SHOTPLUG_CTL,
+ Mask_Unset => SHOTPLUG_CTL_DETECT_MASK,
+ Mask_Set => SHOTPLUG_CTL_HPD_INPUT_ENABLE (Port) or
+ SHOTPLUG_CTL_HPD_STATUS (Port)); -- clear status
+ else
+ Registers.Unset_Mask
+ (Register => Registers.SHOTPLUG_CTL,
+ Mask => SHOTPLUG_CTL_DETECT_MASK or
+ SHOTPLUG_CTL_HPD_INPUT_ENABLE (Port));
+ end if;
+ end loop;
+ end Initialize;
+
+ procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean)
+ is
+ Ctl32 : Word32;
+ begin
+ if Port_Cfg.Display = VGA then
+ Registers.Read (Registers.PCH_ADPA, Ctl32, Verbose => False);
+ Ctl32 := Ctl32 and PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+ Detected := Ctl32 = PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+ if Ctl32 /= 0 then
+ Registers.Set_Mask
+ (Register => Registers.PCH_ADPA,
+ Mask => PCH_ADPA_CRT_HPD_CHANNEL_MASK);
+ end if;
+ elsif Config.Has_HOTPLUG_CTL and then Port_Cfg.Port = DIGI_A then
+ Registers.Read (Registers.HOTPLUG_CTL, Ctl32, Verbose => False);
+ Detected := (Ctl32 and HOTPLUG_CTL_DDI_A_HPD_LONG_DETECT) /= 0;
+
+ if (Ctl32 and HOTPLUG_CTL_DDI_A_HPD_STATUS) /= 0 then
+ Registers.Set_Mask
+ (Register => Registers.HOTPLUG_CTL,
+ Mask => HOTPLUG_CTL_DDI_A_HPD_STATUS);
+ end if;
+ elsif Port_Cfg.Port in DIGI_A .. DIGI_D then
+ Registers.Read (Registers.SHOTPLUG_CTL, Ctl32, Verbose => False);
+ Detected :=
+ (Ctl32 and SHOTPLUG_CTL_LONG_DETECT (Port_Cfg.Port)) /= 0;
+
+ if (Ctl32 and SHOTPLUG_CTL_HPD_STATUS (Port_Cfg.Port)) /= 0 then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.SHOTPLUG_CTL,
+ Mask_Unset => SHOTPLUG_CTL_DETECT_MASK,
+ Mask_Set => SHOTPLUG_CTL_HPD_STATUS (Port_Cfg.Port));
+ end if;
+ else
+ Detected := False;
+ end if;
+ end Hotplug_Detect;
+
+end HW.GFX.GMA.Port_Detect;
diff --git a/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.adb b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.adb
new file mode 100644
index 0000000..b46c29d
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.adb
@@ -0,0 +1,235 @@
+--
+-- Copyright (C) 2014-2016 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; version 2 of the License.
+--
+-- 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 GNAT.Source_Info;
+
+with HW.Time;
+with HW.Debug;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Power_And_Clocks_Haswell is
+
+ PWR_WELL_CTL_ENABLE_REQUEST : constant := 1 * 2 ** 31;
+ PWR_WELL_CTL_DISABLE_REQUEST : constant := 0 * 2 ** 31;
+ PWR_WELL_CTL_STATE_ENABLED : constant := 1 * 2 ** 30;
+
+ ----------------------------------------------------------------------------
+
+ SRD_CTL_ENABLE : constant := 1 * 2 ** 31;
+ SRD_STATUS_STATE_MASK : constant := 7 * 2 ** 29;
+
+ type Pipe is (EDP, A, B, C);
+ type SRD_Regs is record
+ CTL : Registers.Registers_Index;
+ STATUS : Registers.Registers_Index;
+ end record;
+ type SRD_Per_Pipe_Regs is array (Pipe) of SRD_Regs;
+ SRD : constant SRD_Per_Pipe_Regs := SRD_Per_Pipe_Regs'
+ (A => SRD_Regs'
+ (CTL => Registers.SRD_CTL_A,
+ STATUS => Registers.SRD_STATUS_A),
+ B => SRD_Regs'
+ (CTL => Registers.SRD_CTL_B,
+ STATUS => Registers.SRD_STATUS_B),
+ C => SRD_Regs'
+ (CTL => Registers.SRD_CTL_C,
+ STATUS => Registers.SRD_STATUS_C),
+ EDP => SRD_Regs'
+ (CTL => Registers.SRD_CTL_EDP,
+ STATUS => Registers.SRD_STATUS_EDP));
+
+ ----------------------------------------------------------------------------
+
+ IPS_CTL_ENABLE : constant := 1 * 2 ** 31;
+ DISPLAY_IPS_CONTROL : constant := 16#19#;
+
+ GT_MAILBOX_READY : constant := 1 * 2 ** 31;
+
+ ----------------------------------------------------------------------------
+
+ procedure PSR_Off
+ is
+ Enabled : Boolean;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.Has_Per_Pipe_SRD then
+ for P in Pipe loop
+ Registers.Is_Set_Mask (SRD (P).CTL, SRD_CTL_ENABLE, Enabled);
+ if Enabled then
+ Registers.Unset_Mask (SRD (P).CTL, SRD_CTL_ENABLE);
+ Registers.Wait_Unset_Mask (SRD (P).STATUS, SRD_STATUS_STATE_MASK);
+
+ pragma Debug (Debug.Put_Line ("Disabled PSR."));
+ end if;
+ end loop;
+ else
+ Registers.Is_Set_Mask (Registers.SRD_CTL, SRD_CTL_ENABLE, Enabled);
+ if Enabled then
+ Registers.Unset_Mask (Registers.SRD_CTL, SRD_CTL_ENABLE);
+ Registers.Wait_Unset_Mask (Registers.SRD_STATUS, SRD_STATUS_STATE_MASK);
+
+ pragma Debug (Debug.Put_Line ("Disabled PSR."));
+ end if;
+ end if;
+ end PSR_Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure GT_Mailbox_Write (MBox : Word32; Value : Word32) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+ Registers.Write (Registers.GT_MAILBOX_DATA, Value);
+ Registers.Write (Registers.GT_MAILBOX, GT_MAILBOX_READY or MBox);
+
+ Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+ Registers.Write (Registers.GT_MAILBOX_DATA, 0);
+ end GT_Mailbox_Write;
+
+ procedure IPS_Off
+ is
+ Enabled : Boolean;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.Has_IPS then
+ Registers.Is_Set_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE, Enabled);
+ if Enabled then
+ if Config.Has_IPS_CTL_Mailbox then
+ GT_Mailbox_Write (DISPLAY_IPS_CONTROL, 0);
+ -- May take up to 42ms.
+ Registers.Wait_Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
+ else
+ Registers.Unset_Mask (Registers.IPS_CTL, IPS_CTL_ENABLE);
+ end if;
+
+ pragma Debug (Debug.Put_Line ("Disabled IPS."));
+ -- We have to wait until the next vblank here.
+ -- 20ms should be enough.
+ Time.M_Delay (20);
+ end if;
+ end if;
+ end IPS_Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure PDW_Off
+ is
+ Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+ Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+ Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+ Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
+
+ if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+ PWR_WELL_CTL_ENABLE_REQUEST) /= 0
+ then
+ Registers.Wait_Set_Mask
+ (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+ end if;
+
+ if (Ctl1 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
+ Registers.Write (Registers.PWR_WELL_CTL_BIOS, PWR_WELL_CTL_DISABLE_REQUEST);
+ end if;
+
+ if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) /= 0 then
+ Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_DISABLE_REQUEST);
+ end if;
+ end PDW_Off;
+
+ procedure PDW_On
+ is
+ Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+ Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+ Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+ Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
+
+ if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+ PWR_WELL_CTL_ENABLE_REQUEST) = 0
+ then
+ Registers.Wait_Unset_Mask
+ (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+ end if;
+
+ if (Ctl2 and PWR_WELL_CTL_ENABLE_REQUEST) = 0 then
+ Registers.Write (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_ENABLE_REQUEST);
+ Registers.Wait_Set_Mask
+ (Registers.PWR_WELL_CTL_DRIVER, PWR_WELL_CTL_STATE_ENABLED);
+ end if;
+ end PDW_On;
+
+ function Need_PDW (Checked_Configs : Configs_Type) return Boolean is
+ begin
+ return (Checked_Configs (Primary).Port /= Disabled and
+ Checked_Configs (Primary).Port /= Internal) or
+ Checked_Configs (Secondary).Port /= Disabled or
+ Checked_Configs (Tertiary).Port /= Disabled;
+ end Need_PDW;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_All_Off is
+ begin
+ -- HSW: disable panel self refresh (PSR) on eDP if enabled
+ -- wait for PSR idling
+ PSR_Off;
+ IPS_Off;
+ end Pre_All_Off;
+
+ procedure Initialize is
+ begin
+ -- HSW: disable power down well
+ PDW_Off;
+ end Initialize;
+
+ procedure Power_Set_To (Configs : Configs_Type) is
+ begin
+ if Need_PDW (Configs) then
+ PDW_On;
+ else
+ PDW_Off;
+ end if;
+ end Power_Set_To;
+
+ procedure Power_Up (Old_Configs, New_Configs : Configs_Type) is
+ begin
+ if not Need_PDW (Old_Configs) and Need_PDW (New_Configs) then
+ PDW_On;
+ end if;
+ end Power_Up;
+
+ procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type)
+ is
+ begin
+ if (Need_PDW (Old_Configs) or Need_PDW (Tmp_Configs)) and
+ not Need_PDW (New_Configs)
+ then
+ PDW_Off;
+ end if;
+ end Power_Down;
+
+end HW.GFX.GMA.Power_And_Clocks_Haswell;
diff --git a/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.ads b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.ads
new file mode 100644
index 0000000..84cf889
--- /dev/null
+++ b/common/haswell_shared/hw-gfx-gma-power_and_clocks_haswell.ads
@@ -0,0 +1,27 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Power_And_Clocks_Haswell is
+
+ procedure PSR_Off;
+
+ procedure Pre_All_Off;
+ procedure Post_All_Off is null;
+
+ procedure Initialize;
+
+ procedure Power_Set_To (Configs : Configs_Type);
+ procedure Power_Up (Old_Configs, New_Configs : Configs_Type);
+ procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type);
+
+end HW.GFX.GMA.Power_And_Clocks_Haswell;
diff --git a/common/hw-gfx-dp_aux_ch.adb b/common/hw-gfx-dp_aux_ch.adb
new file mode 100644
index 0000000..2f8b982
--- /dev/null
+++ b/common/hw-gfx-dp_aux_ch.adb
@@ -0,0 +1,392 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.DP_Defs;
+
+use type HW.Word8;
+use type HW.GFX.DP_Defs.Aux_Message_Command;
+
+package body HW.GFX.DP_Aux_Ch is
+
+ DP_AUX_I2C_WRITE : constant := 16#0#;
+ DP_AUX_I2C_READ : constant := 16#1#;
+ DP_AUX_I2C_WR_STATUS_REQ : constant := 16#2#;
+ DP_AUX_I2C_MOT : constant := 16#4#;
+ DP_AUX_NATIVE_WRITE : constant := 16#8#;
+ DP_AUX_NATIVE_READ : constant := 16#9#;
+
+ procedure Fill_Aux_Request
+ (Request : out DP_Defs.Aux_Request;
+ Command : in DP_Defs.Aux_Message_Command;
+ Address : in DP_Defs.Aux_Message_Address;
+ Length : in DP_Defs.Aux_Payload_Length)
+ is
+ begin
+ Request :=
+ (0 => Shift_Left (Word8 (Command), 4) or
+ Word8 (Shift_Right (Word32 (Address), 16)),
+ 1 => Word8 (Shift_Right (Word32 (Address), 8) and 16#ff#),
+ 2 => Word8 (Shift_Right (Word32 (Address), 0) and 16#ff#),
+ 3 => Word8 (Length) - 1,
+ others => 0); -- Don't care
+ end Fill_Aux_Request;
+
+ function Is_Empty (Request : DP_Defs.Aux_Request) return Boolean is
+ begin
+ return Request (3) = 16#ff#;
+ end Is_Empty;
+
+ function Is_Aux_Ack (Response : DP_Defs.Aux_Response) return Boolean
+ with
+ Depends => (Is_Aux_Ack'Result => Response)
+ is
+ begin
+ return (Response (0) and 16#30#) = 16#00#;
+ end Is_Aux_Ack;
+
+ function Is_Aux_Defer (Response : DP_Defs.Aux_Response) return Boolean is
+ begin
+ return (Response (0) and 16#30#) = 16#20#;
+ end Is_Aux_Defer;
+
+ function Is_I2C_Ack (Response : DP_Defs.Aux_Response) return Boolean
+ with
+ Depends => (Is_I2C_Ack'Result => Response)
+ is
+ begin
+ return (Response (0) and 16#c0#) = 16#00#;
+ end Is_I2C_Ack;
+
+ function Is_I2C_Defer (Response : DP_Defs.Aux_Response) return Boolean is
+ begin
+ return (Response (0) and 16#c0#) = 16#80#;
+ end Is_I2C_Defer;
+
+ ----------------------------------------------------------------------------
+
+ procedure Do_Aux_Request
+ (Port : in T;
+ Request : in DP_Defs.Aux_Request;
+ Request_Length : in DP_Defs.Aux_Request_Length;
+ Response : out DP_Defs.Aux_Response;
+ Response_Length : out DP_Defs.Aux_Response_Length;
+ Success : out Boolean)
+ is
+ begin
+ for Try in Positive range 1 .. 32 loop
+ Aux_Request
+ (Port => Port,
+ Request => Request,
+ Request_Length => Request_Length,
+ Response => Response,
+ Response_Length => Response_Length,
+ Success => Success);
+
+ exit when not (Success and Is_Aux_Defer (Response));
+ Time.U_Delay (500);
+ end loop;
+
+ Success := Success and then Is_Aux_Ack (Response);
+ end Do_Aux_Request;
+
+ ----------------------------------------------------------------------------
+
+ procedure Aux_Read
+ (Port : in T;
+ Address : in DP_Defs.Aux_Message_Address;
+ Length : in out DP_Defs.Aux_Payload_Length;
+ Data : out DP_Defs.Aux_Payload;
+ Success : out Boolean)
+ is
+ Request : DP_Defs.Aux_Request;
+ Response : DP_Defs.Aux_Response;
+ Response_Length : DP_Defs.Aux_Response_Length;
+ begin
+ Data := (others => 0); -- Initialize
+
+ Fill_Aux_Request
+ (Request => Request,
+ Command => DP_AUX_NATIVE_READ,
+ Address => Address,
+ Length => Length);
+
+ Do_Aux_Request
+ (Port => Port,
+ Request => Request,
+ Request_Length => 4,
+ Response => Response,
+ Response_Length => Response_Length,
+ Success => Success);
+
+ Success := Success and then Response_Length > 1;
+ if Success then
+ pragma Assert (Response_Length > 1);
+ Length := Response_Length - 1;
+ Data (0 .. Length - 1) := Response (1 .. Length);
+ end if;
+ end Aux_Read;
+
+ procedure Aux_Write
+ (Port : in T;
+ Address : in DP_Defs.Aux_Message_Address;
+ Length : in DP_Defs.Aux_Payload_Length;
+ Data : in DP_Defs.Aux_Payload;
+ Success : out Boolean)
+ is
+ Request : DP_Defs.Aux_Request;
+
+ Ignored_Response : DP_Defs.Aux_Response;
+ Ignored_Response_Length : DP_Defs.Aux_Response_Length;
+ begin
+ Fill_Aux_Request
+ (Request => Request,
+ Command => DP_AUX_NATIVE_WRITE,
+ Address => Address,
+ Length => Length);
+ Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1);
+
+ pragma Warnings (GNATprove, Off,
+ "unused assignment to ""Ignored_Response*""",
+ Reason => "No response expected here");
+ Do_Aux_Request
+ (Port => Port,
+ Request => Request,
+ Request_Length => 4 + Length,
+ Response => Ignored_Response,
+ Response_Length => Ignored_Response_Length,
+ Success => Success);
+ end Aux_Write;
+
+ ----------------------------------------------------------------------------
+
+ procedure I2C_Out_Packet
+ (Port : in T;
+ Command : in DP_Defs.Aux_Message_Command;
+ Address : in DP_Defs.Aux_Message_Address;
+ Length : in DP_Defs.Aux_Payload_Length;
+ Data : in DP_Defs.Aux_Payload;
+ Success : out Boolean)
+ is
+ Request : DP_Defs.Aux_Request;
+
+ Response : DP_Defs.Aux_Response;
+ Ignored_Response_Length : DP_Defs.Aux_Response_Length;
+ begin
+ Fill_Aux_Request
+ (Request => Request,
+ Command => Command,
+ Address => Address,
+ Length => Length);
+ Request (4 .. Length + 4 - 1) := Data (0 .. Length - 1);
+ for Try in Positive range 1 .. 7 loop
+ pragma Warnings (GNATprove, Off,
+ "unused assignment to ""Ignored_Response_Length""",
+ Reason => "No response expected here");
+ Do_Aux_Request
+ (Port => Port,
+ Request => Request,
+ Request_Length => (if Is_Empty (Request) then 3 else 4 + Length),
+ Response => Response,
+ Response_Length => Ignored_Response_Length,
+ Success => Success);
+ exit when not (Success and Is_I2C_Defer (Response));
+
+ -- Command was already AUX-acked. Thus, only query for
+ -- new status from now on until we get I2C-acked too.
+ Fill_Aux_Request
+ (Request => Request,
+ Command => (Command and DP_AUX_I2C_MOT) or DP_AUX_I2C_WR_STATUS_REQ,
+ Address => Address,
+ Length => 0);
+ Time.U_Delay (500);
+ end loop;
+ Success := Success and then Is_I2C_Ack (Response);
+ end I2C_Out_Packet;
+
+ procedure I2C_In_Packet
+ (Port : in T;
+ Command : in DP_Defs.Aux_Message_Command;
+ Address : in DP_Defs.Aux_Message_Address;
+ Length : in DP_Defs.Aux_Payload_Length;
+ Response : out DP_Defs.Aux_Response;
+ Response_Length : out DP_Defs.Aux_Response_Length;
+ Success : out Boolean)
+ is
+ Request : DP_Defs.Aux_Request;
+ begin
+ Fill_Aux_Request
+ (Request => Request,
+ Command => Command,
+ Address => Address,
+ Length => Length);
+ for Try in Positive range 1 .. 7 loop
+ Do_Aux_Request
+ (Port => Port,
+ Request => Request,
+ Request_Length => (if Is_Empty (Request) then 3 else 4),
+ Response => Response,
+ Response_Length => Response_Length,
+ Success => Success);
+ exit when not (Success and Is_I2C_Defer (Response));
+
+ Time.U_Delay (500);
+ end loop;
+ Success := Success and then Is_I2C_Ack (Response);
+ end I2C_In_Packet;
+
+ procedure I2C_Empty_Packet
+ (Port : in T;
+ Command : in DP_Defs.Aux_Message_Command;
+ Address : in DP_Defs.Aux_Message_Address;
+ Success : out Boolean)
+ is
+ Ignored_Response : DP_Defs.Aux_Response;
+ Ignored_Response_Length : DP_Defs.Aux_Response_Length;
+ begin
+ pragma Warnings (GNATprove, Off,
+ "unused assignment to ""Ignored_Response*""",
+ Reason => "No response expected here");
+ I2C_In_Packet
+ (Port => Port,
+ Command => Command,
+ Address => Address,
+ Length => 0,
+ Response => Ignored_Response,
+ Response_Length => Ignored_Response_Length,
+ Success => Success);
+ end I2C_Empty_Packet;
+
+ ----------------------------------------------------------------------------
+
+ procedure Do_I2C_Write
+ (Port : in T;
+ Address : in I2C.Transfer_Address;
+ Length : in DP_Defs.Aux_Payload_Length;
+ Data : in DP_Defs.Aux_Payload;
+ Success : out Boolean)
+ is
+ Ignored_Success : Boolean;
+ begin
+ -- I2C address "start" packet
+ I2C_Empty_Packet
+ (Port => Port,
+ Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
+ Address => DP_Defs.Aux_Message_Address (Address),
+ Success => Success);
+
+ if Success then
+ I2C_Out_Packet
+ (Port => Port,
+ Command => DP_AUX_I2C_WRITE or DP_AUX_I2C_MOT,
+ Address => DP_Defs.Aux_Message_Address (Address),
+ Length => Length,
+ Data => Data,
+ Success => Success);
+
+ pragma Warnings
+ (GNATprove, Off, "unused assignment to ""Ignored_Success""",
+ Reason => "Doesn't matter as long as the transfer itself succeeds");
+ -- I2C address "stop" packet
+ I2C_Empty_Packet
+ (Port => Port,
+ Command => DP_AUX_I2C_WRITE,
+ Address => DP_Defs.Aux_Message_Address (Address),
+ Success => Ignored_Success);
+ end if;
+ end Do_I2C_Write;
+
+ procedure Do_I2C_Read
+ (Port : in T;
+ Address : in I2C.Transfer_Address;
+ Length : in out I2C.Transfer_Length;
+ Data : in out I2C.Transfer_Data;
+ Success : out Boolean)
+ is
+ Xfered : Natural := 0;
+ Chunk : DP_Defs.Aux_Payload_Length := DP_Defs.Aux_Payload_Length'Last;
+
+ Tries : Natural := 0;
+ Max_Tries : constant := 4;
+
+ Response : DP_Defs.Aux_Response;
+ Response_Length : DP_Defs.Aux_Response_Length;
+
+ Ignored_Success : Boolean;
+ begin
+ -- I2C address "start" packet
+ I2C_Empty_Packet
+ (Port => Port,
+ Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
+ Address => DP_Defs.Aux_Message_Address (Address),
+ Success => Success);
+
+ while Success and then (Xfered < Length and Tries < Max_Tries) loop
+ I2C_In_Packet
+ (Port => Port,
+ Command => DP_AUX_I2C_READ or DP_AUX_I2C_MOT,
+ Address => DP_Defs.Aux_Message_Address (Address),
+ Length => Natural'Min (Chunk, Length - Xfered),
+ Response => Response,
+ Response_Length => Response_Length,
+ Success => Success);
+
+ if Success and then Response_Length >= 2 then
+ Chunk := Natural'Min (Response_Length - 1, Length - Xfered);
+ Data (Xfered .. Xfered + Chunk - 1) := Response (1 .. Chunk);
+ Xfered := Xfered + Chunk;
+ Tries := 0;
+ else
+ Tries := Tries + 1;
+ end if;
+ pragma Loop_Invariant (Xfered <= Length);
+ end loop;
+
+ if Success then
+ pragma Warnings
+ (GNATprove, Off, "unused assignment to ""Ignored_Success""",
+ Reason => "Doesn't matter as long as the transfer itself succeeds");
+ -- I2C address "stop" packet
+ I2C_Empty_Packet
+ (Port => Port,
+ Command => DP_AUX_I2C_READ,
+ Address => DP_Defs.Aux_Message_Address (Address),
+ Success => Ignored_Success);
+ end if;
+
+ Success := Success and then Tries < Max_Tries;
+ Length := Xfered;
+ end Do_I2C_Read;
+
+ ----------------------------------------------------------------------------
+
+ procedure I2C_Read
+ (Port : in T;
+ Address : in I2C.Transfer_Address;
+ Length : in out I2C.Transfer_Length;
+ Data : out I2C.Transfer_Data;
+ Success : out Boolean)
+ is
+ Index_Payload : DP_Defs.Aux_Payload;
+ begin
+ Data := (others => 16#00#);
+
+ Index_Payload := (others => 16#00#); -- send index 0
+ Do_I2C_Write (Port, Address, 1, Index_Payload, Success);
+
+ if Success then
+ Do_I2C_Read (Port, Address, Length, Data, Success);
+ end if;
+ end I2C_Read;
+
+end HW.GFX.DP_Aux_Ch;
diff --git a/common/hw-gfx-dp_aux_ch.ads b/common/hw-gfx-dp_aux_ch.ads
new file mode 100644
index 0000000..c65f860
--- /dev/null
+++ b/common/hw-gfx-dp_aux_ch.ads
@@ -0,0 +1,54 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.I2C;
+with HW.GFX.DP_Defs;
+
+private generic
+
+ type T (<>) is limited private;
+
+ with procedure Aux_Request
+ (Port : in T;
+ Request : in DP_Defs.Aux_Request;
+ Request_Length : in DP_Defs.Aux_Request_Length;
+ Response : out DP_Defs.Aux_Response;
+ Response_Length : out DP_Defs.Aux_Response_Length;
+ Success : out Boolean);
+
+package HW.GFX.DP_Aux_Ch is
+
+ procedure Aux_Read
+ (Port : in T;
+ Address : in DP_Defs.Aux_Message_Address;
+ Length : in out DP_Defs.Aux_Payload_Length;
+ Data : out DP_Defs.Aux_Payload;
+ Success : out Boolean);
+
+ procedure Aux_Write
+ (Port : in T;
+ Address : in DP_Defs.Aux_Message_Address;
+ Length : in DP_Defs.Aux_Payload_Length;
+ Data : in DP_Defs.Aux_Payload;
+ Success : out Boolean);
+
+ ----------------------------------------------------------------------------
+
+ procedure I2C_Read
+ (Port : in T;
+ Address : in I2C.Transfer_Address;
+ Length : in out I2C.Transfer_Length;
+ Data : out I2C.Transfer_Data;
+ Success : out Boolean);
+
+end HW.GFX.DP_Aux_Ch;
diff --git a/common/hw-gfx-dp_defs.ads b/common/hw-gfx-dp_defs.ads
new file mode 100644
index 0000000..9332f9e
--- /dev/null
+++ b/common/hw-gfx-dp_defs.ads
@@ -0,0 +1,32 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.DP_Defs is
+
+ type Aux_Message_Command is mod 2 ** 4;
+ type Aux_Message_Address is mod 2 ** 20;
+
+ subtype Aux_Payload_Length is Natural range 0 .. 16;
+ subtype Aux_Payload_Index is Natural range 0 .. Aux_Payload_Length'Last - 1;
+ subtype Aux_Payload is Buffer (Aux_Payload_Index);
+
+ subtype Aux_Request_Length is Natural range 3 .. 20;
+ subtype Aux_Request_Index is Natural range 0 .. Aux_Request_Length'Last - 1;
+ subtype Aux_Request is Buffer (Aux_Request_Index);
+
+ subtype Aux_Response_Length is Natural range 1 .. 17;
+ subtype Aux_Response_Index is
+ Natural range 0 .. Aux_Response_Length'Last - 1;
+ subtype Aux_Response is Buffer (Aux_Response_Index);
+
+end HW.GFX.DP_Defs;
diff --git a/common/hw-gfx-dp_info.adb b/common/hw-gfx-dp_info.adb
new file mode 100644
index 0000000..7e47885
--- /dev/null
+++ b/common/hw-gfx-dp_info.adb
@@ -0,0 +1,380 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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;
+
+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;
+
+ procedure Minimum_Lane_Count
+ (Link : in out DP_Link;
+ Mode : in Mode_Type;
+ Success : out Boolean)
+ with
+ Depends => ((Link, Success) => (Link, Mode))
+ is
+ function Link_Pixel_Per_Second
+ (Link_Rate : DP_Bandwidth)
+ return Positive
+ with
+ Post => Pos64 (Link_Pixel_Per_Second'Result) <=
+ ((DP_Symbol_Rate_Type'Last * 8) / 3) / BPC_Type'First
+ is
+ begin
+ -- Link_Rate is brutto with 8/10 bit symbols; three colors
+ pragma Assert (Positive (DP_Symbol_Rate (Link_Rate)) <= (Positive'Last / 8) * 3);
+ pragma Assert ((Int64 (DP_Symbol_Rate (Link_Rate)) * 8) / 3
+ >= Int64 (BPC_Type'Last));
+ return Positive
+ (((Int64 (DP_Symbol_Rate (Link_Rate)) * 8) / 3)
+ / Int64 (Mode.BPC));
+ end Link_Pixel_Per_Second;
+
+ Count : Natural;
+ begin
+ Count := Link_Pixel_Per_Second (Link.Bandwidth);
+ Count := (Positive (Mode.Dotclock) + Count - 1) / Count;
+
+ 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 (Success, Debug.Put ("Trying DP settings: Symbol Rate = "));
+ pragma Debug (Success, Debug.Put_Int32
+ (Int32 (DP_Symbol_Rate (Link.Bandwidth))));
+ pragma Debug (Success, Debug.Put ("; Lane Count = "));
+ pragma Debug (Success, Debug.Put_Int32
+ (Int32 (Lane_Count_As_Integer (Link.Lane_Count))));
+ pragma Debug (Success, Debug.New_Line);
+ pragma Debug (Success, Debug.New_Line);
+
+ 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;
+
+ pragma Debug (Success, Debug.Put ("Trying DP settings: Symbol Rate = "));
+ pragma Debug (Success, Debug.Put_Int32
+ (Int32 (DP_Symbol_Rate (Link.Bandwidth))));
+ pragma Debug (Success, Debug.Put ("; Lane Count = "));
+ pragma Debug (Success, Debug.Put_Int32
+ (Int32 (Lane_Count_As_Integer (Link.Lane_Count))));
+ pragma Debug (Success, Debug.New_Line);
+ pragma Debug (Success, Debug.New_Line);
+ end Next_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 ** 36;
+ subtype Calc_N_Type is Int64 range 0 .. 2 ** 36;
+ 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;
diff --git a/common/hw-gfx-dp_info.ads b/common/hw-gfx-dp_info.ads
new file mode 100644
index 0000000..d793dff
--- /dev/null
+++ b/common/hw-gfx-dp_info.ads
@@ -0,0 +1,135 @@
+--
+-- Copyright (C) 2015 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; version 2 of the License.
+--
+-- 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 HW.GFX.DP_Aux_Ch;
+
+private generic
+
+ type T (<>) is limited private;
+
+ with package Aux_Ch is new DP_Aux_Ch (T => T, others => <>);
+
+package HW.GFX.DP_Info is
+
+ type DP_Voltage_Swing is (VS_Level_0, VS_Level_1, VS_Level_2, VS_Level_3);
+
+ type DP_Pre_Emph is (Emph_Level_0, Emph_Level_1, Emph_Level_2, Emph_Level_3);
+
+ type Train_Set is record
+ Voltage_Swing : DP_Voltage_Swing;
+ Pre_Emph : DP_Pre_Emph;
+ end record;
+
+ type Training_Pattern is (TP_1, TP_2, TP_3, TP_Idle, TP_None);
+
+ ----------------------------------------------------------------------------
+
+ type Lane_Index is new Natural range 0 .. 3;
+
+ type Lane_Status is record
+ CR_Done : Boolean;
+ Channel_EQ_Done : Boolean;
+ Symbol_Locked : Boolean;
+ Reserved : Boolean;
+ end record;
+ for Lane_Status use record
+ CR_Done at 16#00# range 0 .. 0;
+ Channel_EQ_Done at 16#00# range 1 .. 1;
+ Symbol_Locked at 16#00# range 2 .. 2;
+ Reserved at 16#00# range 3 .. 3;
+ end record;
+ type Lanes_Status is array (Lane_Index) of Lane_Status;
+ pragma Pack (Lanes_Status);
+
+ type Adjust_Request is record
+ Voltage_Swing : DP_Voltage_Swing;
+ Pre_Emph : DP_Pre_Emph;
+ end record;
+ for Adjust_Request use record
+ Voltage_Swing at 16#00# range 0 .. 1;
+ Pre_Emph at 16#00# range 2 .. 3;
+ end record;
+ type Adjust_Requests_Array is array (Lane_Index) of Adjust_Request;
+ pragma Pack (Adjust_Requests_Array);
+
+ type Link_Status is record
+ Lanes : Lanes_Status;
+ Interlane_Align_Done : Boolean;
+ Adjust_Requests : Adjust_Requests_Array;
+ end record;
+ for Link_Status use record
+ Lanes at 16#00# range 0 .. 15;
+ Interlane_Align_Done at 16#02# range 0 .. 0;
+ Adjust_Requests at 16#04# range 0 .. 15;
+ end record;
+
+ ----------------------------------------------------------------------------
+
+ procedure Read_Caps
+ (Link : in out DP_Link;
+ Port : in T;
+ Success : out Boolean);
+
+ procedure Preferred_Link_Setting
+ (Link : in out DP_Link;
+ Mode : in Mode_Type;
+ Success : out Boolean);
+
+ procedure Next_Link_Setting
+ (Link : in out DP_Link;
+ Mode : in Mode_Type;
+ Success : out Boolean);
+
+ ----------------------------------------------------------------------------
+
+ M_N_Max : constant := 2 ** 24 - 1;
+
+ subtype M_Type is Int64 range 0 .. M_N_Max;
+ subtype N_Type is Int64 range 0 .. M_N_Max;
+
+ 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);
+
+ ----------------------------------------------------------------------------
+
+ procedure Read_Link_Status
+ (Port : in T;
+ Status : out Link_Status;
+ Success : out Boolean);
+
+ function All_CR_Done
+ (Status : Link_Status;
+ Link : DP_Link)
+ return Boolean;
+
+ function All_EQ_Done
+ (Status : Link_Status;
+ Link : DP_Link)
+ return Boolean;
+
+ function Max_Requested_VS
+ (Status : Link_Status;
+ Link : DP_Link)
+ return DP_Voltage_Swing;
+
+ function Max_Requested_Emph
+ (Status : Link_Status;
+ Link : DP_Link)
+ return DP_Pre_Emph;
+
+end HW.GFX.DP_Info;
diff --git a/common/hw-gfx-dp_training.adb b/common/hw-gfx-dp_training.adb
new file mode 100644
index 0000000..4ba548a
--- /dev/null
+++ b/common/hw-gfx-dp_training.adb
@@ -0,0 +1,412 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.Time;
+with HW.GFX.DP_Defs;
+
+package body HW.GFX.DP_Training is
+
+ pragma Warnings (GNATprove, Off, "unused initial value of ""Port""*",
+ Reason => "Needed for a common interface");
+ function Training_Set
+ (Port : T;
+ Train_Set : DP_Info.Train_Set)
+ return Word8
+ is
+ use type DP_Info.DP_Voltage_Swing;
+ use type DP_Info.DP_Pre_Emph;
+ use type Word8;
+ Value : Word8;
+ begin
+ case Train_Set.Voltage_Swing is
+ when DP_Info.VS_Level_0 => Value := 16#00#;
+ when DP_Info.VS_Level_1 => Value := 16#01#;
+ when DP_Info.VS_Level_2 => Value := 16#02#;
+ when DP_Info.VS_Level_3 => Value := 16#03#;
+ end case;
+ if Train_Set.Voltage_Swing = Max_V_Swing (Port) then
+ Value := Value or 16#04#;
+ end if;
+
+ case Train_Set.Pre_Emph is
+ when DP_Info.Emph_Level_0 => Value := Value or 16#00#;
+ when DP_Info.Emph_Level_1 => Value := Value or 16#08#;
+ when DP_Info.Emph_Level_2 => Value := Value or 16#10#;
+ when DP_Info.Emph_Level_3 => Value := Value or 16#18#;
+ end case;
+ if Train_Set.Pre_Emph = Max_Pre_Emph (Port, Train_Set) then
+ Value := Value or 16#20#;
+ end if;
+
+ return Value;
+ end Training_Set;
+ pragma Warnings (GNATprove, On, "unused initial value of ""Port""*");
+
+ ----------------------------------------------------------------------------
+
+ function Lane_Count (Link : DP_Link) return Positive
+ with
+ Post => Lane_Count'Result <= 4
+ is
+ begin
+ return Positive (Lane_Count_As_Integer (Link.Lane_Count));
+ end Lane_Count;
+
+ procedure Sink_Init
+ (Port : in Aux_T;
+ Link : in DP_Link;
+ Success : out Boolean)
+ is
+ use type Word8;
+ function Link_Rate_As_Word8 is new Ada.Unchecked_Conversion
+ (Source => DP_Bandwidth, Target => Word8);
+ Data : DP_Defs.Aux_Payload;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Data :=
+ (0 => Link_Rate_As_Word8 (Link.Bandwidth),
+ 1 => Word8 (Lane_Count (Link)),
+ others => 0); -- Don't care
+
+ if Link.Enhanced_Framing then
+ Data (1) := Data (1) or 16#80#;
+ end if;
+
+ Aux_Ch.Aux_Write
+ (Port => Port,
+ Address => 16#00100#, -- LINK_BW_SET, LANE_COUNT_SET
+ Length => 2,
+ Data => Data,
+ Success => Success);
+ Success := Success or Link.Opportunistic_Training;
+
+ if Success then
+ Data (0) := 16#00#; -- no downspread
+ Data (1) := 16#01#; -- ANSI8B10B coding
+
+ Aux_Ch.Aux_Write
+ (Port => Port,
+ Address => 16#00107#, -- DOWNSPREAD_CTRL,
+ Length => 2, -- MAIN_LINK_CHANNEL_CODING_SET
+ Data => Data,
+ Success => Success);
+ Success := Success or Link.Opportunistic_Training;
+ end if;
+ end Sink_Init;
+
+ procedure Sink_Set_Training_Pattern
+ (DP : in Aux_T;
+ Link : in DP_Link;
+ Pattern : in DP_Info.Training_Pattern;
+ Success : out Boolean)
+ is
+ use type DP_Info.Training_Pattern;
+
+ type TP_Array is array (DP_Info.Training_Pattern) of Word8;
+ TP : constant TP_Array := TP_Array'
+ (DP_Info.TP_1 => 16#21#, DP_Info.TP_2 => 16#22#, DP_Info.TP_3 => 16#23#,
+ DP_Info.TP_Idle => 16#00#, DP_Info.TP_None => 16#00#);
+
+ Data : DP_Defs.Aux_Payload;
+ Length : Positive := 1;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Data :=
+ (0 => TP (Pattern),
+ others => 0); -- Don't care
+
+ if Pattern < DP_Info.TP_Idle then
+ Length := Length + Lane_Count (Link);
+ end if;
+ Aux_Ch.Aux_Write
+ (Port => DP,
+ Address => 16#00102#, -- TRAINING_PATTERN_SET
+ Length => Length,
+ Data => Data,
+ Success => Success);
+ end Sink_Set_Training_Pattern;
+
+ procedure Sink_Set_Signal_Levels
+ (Port : in T;
+ DP : in Aux_T;
+ Link : in DP_Link;
+ Train_Set : in DP_Info.Train_Set;
+ Success : out Boolean)
+ is
+ Data : DP_Defs.Aux_Payload;
+ T_Set : constant Word8 := Training_Set (Port, Train_Set);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Data := (others => 0); -- Initialize
+ Data (0 .. Lane_Count (Link) - 1) := (others => T_Set);
+
+ Aux_Ch.Aux_Write
+ (Port => DP,
+ Address => 16#00103#, -- TRAINING_LANEx_SET
+ Length => Lane_Count (Link),
+ Data => Data,
+ Success => Success);
+ end Sink_Set_Signal_Levels;
+
+ pragma Warnings (GNATprove, Off, "unused initial value of ""Port""*",
+ Reason => "Needed for a common interface");
+ procedure Sink_Adjust_Training
+ (Port : in T;
+ DP : in Aux_T;
+ Link : in DP_Link;
+ Train_Set : in out DP_Info.Train_Set;
+ CR_Done : in out Boolean;
+ EQ_Done : out Boolean;
+ Success : out Boolean)
+ is
+ use type DP_Info.DP_Voltage_Swing;
+ use type DP_Info.DP_Pre_Emph;
+
+ Status : DP_Info.Link_Status;
+ CR_Was_Done : constant Boolean := CR_Done;
+
+ pragma Warnings
+ (GNATprove, Off, "subprogram ""Dump_Link_Status"" has no effect*",
+ Reason => "It's only used for debugging");
+ procedure Dump_Link_Status
+ is
+ begin
+ Debug.New_Line;
+ Debug.Put_Line ("Link Status:");
+
+ for Lane in DP_Info.Lane_Index range 0
+ .. DP_Info.Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
+ loop
+ Debug.Put (" Lane");
+ Debug.Put_Int8 (Int8 (Lane));
+ Debug.Put_Line (":");
+
+ Debug.Put_Line (" CR_Done : " &
+ (if Status.Lanes (Lane).CR_Done then "1" else "0"));
+ Debug.Put_Line (" Channel_EQ_Done: " &
+ (if Status.Lanes (Lane).Channel_EQ_Done then "1" else "0"));
+ Debug.Put_Line (" Symbol_Locked : " &
+ (if Status.Lanes (Lane).Symbol_Locked then "1" else "0"));
+ end loop;
+
+ Debug.Put_Line (" Interlane_Align_Done: " &
+ (if Status.Interlane_Align_Done then "1" else "0"));
+
+ for Lane in DP_Info.Lane_Index range 0
+ .. DP_Info.Lane_Index (Lane_Count_As_Integer (Link.Lane_Count) - 1)
+ loop
+ Debug.Put (" Adjust");
+ Debug.Put_Int8 (Int8 (Lane));
+ Debug.Put_Line (":");
+
+ Debug.Put (" Voltage_Swing: ");
+ Debug.Put_Int8 (Int8 (DP_Info.DP_Voltage_Swing'Pos
+ (Status.Adjust_Requests (Lane).Voltage_Swing)));
+ Debug.New_Line;
+ Debug.Put (" Pre_Emph : ");
+ Debug.Put_Int8 (Int8 (DP_Info.DP_Pre_Emph'Pos
+ (Status.Adjust_Requests (Lane).Pre_Emph)));
+ Debug.New_Line;
+ end loop;
+
+ Debug.New_Line;
+ end Dump_Link_Status;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ DP_Info.Read_Link_Status
+ (Port => DP,
+ Status => Status,
+ Success => Success);
+
+ pragma Debug (Success, Dump_Link_Status);
+
+ CR_Done := Success and then DP_Info.All_CR_Done (Status, Link);
+ EQ_Done := Success and then DP_Info.All_EQ_Done (Status, Link);
+ Success := Success and then (CR_Done or not CR_Was_Done);
+
+ if Success and not CR_Done then
+ Train_Set.Voltage_Swing :=
+ DP_Info.Max_Requested_VS (Status, Link);
+ if Train_Set.Voltage_Swing > Max_V_Swing (Port)
+ then
+ Train_Set.Voltage_Swing := Max_V_Swing (Port);
+ end if;
+ end if;
+
+ -- According to DP spec, only change preemphasis during channel
+ -- equalization. What to do if sink requests it during clock recovery?
+ -- Linux always accepts new values from the sink, we don't, for now.
+ if Success and then (CR_Was_Done and not EQ_Done) then
+ Train_Set.Pre_Emph :=
+ DP_Info.Max_Requested_Emph (Status, Link);
+ if Train_Set.Pre_Emph > Max_Pre_Emph (Port, Train_Set)
+ then
+ Train_Set.Pre_Emph := Max_Pre_Emph (Port, Train_Set);
+ end if;
+ end if;
+ end Sink_Adjust_Training;
+ pragma Warnings (GNATprove, On, "unused initial value of ""Port""*");
+
+ ----------------------------------------------------------------------------
+
+ procedure Train_DP
+ (Port : in T;
+ Link : in DP_Link;
+ Success : out Boolean)
+ is
+ use type DP_Info.DP_Voltage_Swing;
+ use type DP_Info.DP_Pre_Emph;
+ use type Word8;
+
+ DP : constant Aux_T := To_Aux (Port);
+
+ Retries : Natural;
+ Max_Retry : constant := 4;
+ CR_Done, EQ_Done : Boolean := False;
+
+ EQ_Pattern : constant DP_Info.Training_Pattern :=
+ (if TPS3_Supported and Link.Receiver_Caps.TPS3_Supported then
+ DP_Info.TP_3
+ else
+ DP_Info.TP_2);
+
+ Train_Set, Last_Train_Set : DP_Info.Train_Set;
+
+ function CR_Delay return Natural is
+ Result : Natural := 100; -- DP spec: 100us
+ begin
+ if Link.Bandwidth = DP_Bandwidth_5_4 and
+ Link.Receiver_Caps.Aux_RD_Interval /= 0
+ then
+ Result := Natural (Link.Receiver_Caps.Aux_RD_Interval) * 4_000;
+ end if;
+ return Result;
+ end CR_Delay;
+
+ function EQ_Delay return Natural is
+ Result : Natural := 400; -- DP spec: 400us
+ begin
+ if Link.Bandwidth = DP_Bandwidth_5_4 and
+ Link.Receiver_Caps.Aux_RD_Interval /= 0
+ then
+ Result := Natural (Link.Receiver_Caps.Aux_RD_Interval) * 4_000;
+ end if;
+ return Result;
+ end EQ_Delay;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Train_Set.Voltage_Swing := DP_Info.DP_Voltage_Swing'First;
+ Train_Set.Pre_Emph := DP_Info.DP_Pre_Emph'First;
+
+ Set_Pattern (Port, Link, DP_Info.TP_1);
+ Set_Signal_Levels (Port, Link, Train_Set);
+
+ pragma Warnings
+ (GNATprove, Off, """Success"" modified by call, but value overwritten*",
+ Reason => "Read first, then overwritten, looks like a false positive");
+ Sink_Init (DP, Link, Success);
+ pragma Warnings
+ (GNATprove, On, """Success"" modified by call, but value overwritten*");
+ if Success then
+ Sink_Set_Training_Pattern (DP, Link, DP_Info.TP_1, Success);
+ end if;
+
+ if Success then
+ Retries := 0;
+ for Tries in 1 .. 32 loop
+ pragma Loop_Invariant (Retries <= Max_Retry);
+
+ Time.U_Delay (CR_Delay);
+
+ Last_Train_Set := Train_Set;
+ Sink_Adjust_Training
+ (Port, DP, Link, Train_Set, CR_Done, EQ_Done, Success);
+ exit when CR_Done or not Success;
+
+ if Train_Set.Voltage_Swing = Last_Train_Set.Voltage_Swing then
+ exit when Retries = Max_Retry;
+ Retries := Retries + 1;
+ else
+ exit when Last_Train_Set.Voltage_Swing = Max_V_Swing (Port);
+ Retries := 0;
+ end if;
+
+ Set_Signal_Levels (Port, Link, Train_Set);
+ Sink_Set_Signal_Levels (Port, DP, Link, Train_Set, Success);
+ exit when not Success;
+ end loop;
+ end if;
+
+ Success := Success and CR_Done;
+
+ if Success then
+ Set_Pattern (Port, Link, EQ_Pattern);
+ Sink_Set_Training_Pattern (DP, Link, EQ_Pattern, Success);
+ end if;
+
+ if Success then
+ Retries := 0;
+ for Tries in 1 .. 32 loop
+ pragma Loop_Invariant (Retries <= Max_Retry);
+
+ Time.U_Delay (EQ_Delay);
+
+ Last_Train_Set := Train_Set;
+ Sink_Adjust_Training
+ (Port, DP, Link, Train_Set, CR_Done, EQ_Done, Success);
+ exit when EQ_Done or not Success;
+
+ if Train_Set.Pre_Emph = Last_Train_Set.Pre_Emph then
+ exit when Retries = Max_Retry;
+ Retries := Retries + 1;
+ else
+ exit when Last_Train_Set.Pre_Emph =
+ Max_Pre_Emph (Port, Last_Train_Set);
+ Retries := 0;
+ end if;
+
+ Set_Signal_Levels (Port, Link, Train_Set);
+ Sink_Set_Signal_Levels (Port, DP, Link, Train_Set, Success);
+ exit when not Success;
+ end loop;
+ end if;
+
+ if Success then
+ if EQ_Done then
+ -- Set_Pattern (TP_None) includes sending the Idle Pattern,
+ -- so tell sink first.
+ Sink_Set_Training_Pattern
+ (DP, Link, DP_Info.TP_None, Success);
+ else
+ Success := False;
+ end if;
+ end if;
+
+ if Success then
+ Set_Pattern (Port, Link, DP_Info.TP_None);
+ else
+ Off (Port);
+ end if;
+ end Train_DP;
+
+end HW.GFX.DP_Training;
diff --git a/common/hw-gfx-dp_training.ads b/common/hw-gfx-dp_training.ads
new file mode 100644
index 0000000..188b9c6
--- /dev/null
+++ b/common/hw-gfx-dp_training.ads
@@ -0,0 +1,57 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.DP_Info;
+with HW.GFX.DP_Aux_Ch;
+
+private generic
+
+ TPS3_Supported : Boolean;
+
+ type T (<>) is limited private;
+ type Aux_T (<>) is limited private;
+
+ with package Aux_Ch is new DP_Aux_Ch (T => Aux_T, others => <>);
+
+ with package DP_Info is new GFX.DP_Info (T => Aux_T, Aux_Ch => Aux_Ch);
+
+ with function To_Aux (Port : T) return Aux_T;
+
+ with function Max_V_Swing (Port : T) return DP_Info.DP_Voltage_Swing;
+
+ with function Max_Pre_Emph
+ (Port : T;
+ Train_Set : DP_Info.Train_Set)
+ return DP_Info.DP_Pre_Emph;
+
+ with procedure Set_Pattern
+ (Port : T;
+ Link : DP_Link;
+ Pattern : DP_Info.Training_Pattern);
+
+ with procedure Set_Signal_Levels
+ (Port : T;
+ Link : DP_Link;
+ Train_Set : DP_Info.Train_Set);
+
+ with procedure Off (Connector : T);
+
+package HW.GFX.DP_Training
+is
+
+ procedure Train_DP
+ (Port : in T;
+ Link : in DP_Link;
+ Success : out Boolean);
+
+end HW.GFX.DP_Training;
diff --git a/common/hw-gfx-edid.adb b/common/hw-gfx-edid.adb
new file mode 100644
index 0000000..da60d54
--- /dev/null
+++ b/common/hw-gfx-edid.adb
@@ -0,0 +1,180 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.Byte;
+use type HW.Pos16;
+use type HW.Word16;
+
+package body HW.GFX.EDID is
+
+ function Checksum_Valid (Raw_EDID : Raw_EDID_Data) return Boolean
+ with
+ Pre => True
+ is
+ Sum : Byte := 16#00#;
+ begin
+ for I in Raw_EDID_Index loop
+ Sum := Sum + Raw_EDID (I);
+ end loop;
+ pragma Debug (Sum /= 16#00#, Debug.Put_Line
+ (GNAT.Source_Info.Enclosing_Entity & ": EDID checksum invalid!"));
+ return Sum = 16#00#;
+ end Checksum_Valid;
+
+ function Valid (Raw_EDID : Raw_EDID_Data) return Boolean
+ is
+ Header_Valid : Boolean;
+ begin
+ Header_Valid :=
+ Raw_EDID (0) = 16#00# and
+ Raw_EDID (1) = 16#ff# and
+ Raw_EDID (2) = 16#ff# and
+ Raw_EDID (3) = 16#ff# and
+ Raw_EDID (4) = 16#ff# and
+ Raw_EDID (5) = 16#ff# and
+ Raw_EDID (6) = 16#ff# and
+ Raw_EDID (7) = 16#00#;
+ pragma Debug (not Header_Valid, Debug.Put_Line
+ (GNAT.Source_Info.Enclosing_Entity & ": EDID header pattern mismatch!"));
+
+ return Header_Valid and then Checksum_Valid (Raw_EDID);
+ end Valid;
+
+ ----------------------------------------------------------------------------
+
+ REVISION : constant := 19;
+ INPUT : constant := 20;
+ INPUT_DIGITAL : constant := 1 * 2 ** 7;
+ INPUT_DIGITAL_DEPTH_SHIFT : constant := 4;
+ INPUT_DIGITAL_DEPTH_MASK : constant := 7 * 2 ** 4;
+ INPUT_DIGITAL_DEPTH_UNDEF : constant := 0 * 2 ** 4;
+ INPUT_DIGITAL_DEPTH_RESERVED : constant := 7 * 2 ** 4;
+
+ ----------------------------------------------------------------------------
+
+ function Read_LE16
+ (Raw_EDID : Raw_EDID_Data;
+ Offset : Raw_EDID_Index)
+ return Word16
+ is
+ begin
+ return Shift_Left (Word16 (Raw_EDID (Offset + 1)), 8) or
+ Word16 (Raw_EDID (Offset));
+ end Read_LE16;
+
+ function Has_Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Boolean
+ is
+ begin
+ return
+ Int64 (Read_LE16 (Raw_EDID, DESCRIPTOR_1)) * 10_000
+ in Frequency_Type and
+ ( Raw_EDID (DESCRIPTOR_1 + 2) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 4) and 16#f0#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 8) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#c0#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 9) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#30#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 3) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 4) and 16#0f#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 5) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 7) and 16#f0#) /= 0) and
+ ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#f0#) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#0c#) /= 0) and
+ ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#0f#) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#03#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 6) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 7) and 16#0f#) /= 0);
+ end Has_Preferred_Mode;
+
+ function Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Mode_Type
+ is
+ Base : constant := DESCRIPTOR_1;
+ Mode : Mode_Type;
+
+ function Read_12
+ (Lower_8, Upper_4 : Raw_EDID_Index;
+ Shift : Natural)
+ return Word16
+ is
+ begin
+ return
+ Word16 (Raw_EDID (Lower_8)) or
+ (Shift_Left (Word16 (Raw_EDID (Upper_4)), Shift) and 16#0f00#);
+ end Read_12;
+
+ function Read_10
+ (Lower_8, Upper_2 : Raw_EDID_Index;
+ Shift : Natural)
+ return Word16
+ is
+ begin
+ return
+ Word16 (Raw_EDID (Lower_8)) or
+ (Shift_Left (Word16 (Raw_EDID (Upper_2)), Shift) and 16#0300#);
+ end Read_10;
+
+ function Read_6
+ (Lower_4 : Raw_EDID_Index;
+ Lower_Shift : Natural;
+ Upper_2 : Raw_EDID_Index;
+ Upper_Shift : Natural)
+ return Word8
+ is
+ begin
+ return
+ (Shift_Right (Word8 (Raw_EDID (Lower_4)), Lower_Shift) and 16#0f#)
+ or
+ (Shift_Left (Word8 (Raw_EDID (Upper_2)), Upper_Shift) and 16#30#);
+ end Read_6;
+ begin
+ Mode := Mode_Type'
+ (Dotclock => Pos64 (Read_LE16 (Raw_EDID, Base)) * 10_000,
+ H_Visible => Pos16 (Read_12 (Base + 2, Base + 4, 4)),
+ H_Sync_Begin => Pos16 (Read_10 (Base + 8, Base + 11, 2)),
+ H_Sync_End => Pos16 (Read_10 (Base + 9, Base + 11, 4)),
+ H_Total => Pos16 (Read_12 (Base + 3, Base + 4, 8)),
+ V_Visible => Pos16 (Read_12 (Base + 5, Base + 7, 4)),
+ V_Sync_Begin => Pos16 (Read_6 (Base + 10, 4, Base + 11, 2)),
+ V_Sync_End => Pos16 (Read_6 (Base + 10, 0, Base + 11, 4)),
+ V_Total => Pos16 (Read_12 (Base + 6, Base + 7, 8)),
+ H_Sync_Active_High => (Raw_EDID (Base + 17) and 16#02#) /= 0,
+ V_Sync_Active_High => (Raw_EDID (Base + 17) and 16#04#) /= 0,
+ BPC =>
+ (if Raw_EDID (REVISION) < 4 or
+ (Raw_EDID (INPUT) and INPUT_DIGITAL) = 16#00# or
+ (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_UNDEF or
+ (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_RESERVED
+ then
+ 0
+ else
+ 4 + 2 * Pos64 (Shift_Right
+ (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK,
+ INPUT_DIGITAL_DEPTH_SHIFT))));
+
+ -- Calculate absolute values from EDID relative values.
+ Mode.H_Sync_Begin := Mode.H_Visible + Mode.H_Sync_Begin;
+ Mode.H_Sync_End := Mode.H_Sync_Begin + Mode.H_Sync_End;
+ Mode.H_Total := Mode.H_Visible + Mode.H_Total;
+ Mode.V_Sync_Begin := Mode.V_Visible + Mode.V_Sync_Begin;
+ Mode.V_Sync_End := Mode.V_Sync_Begin + Mode.V_Sync_End;
+ Mode.V_Total := Mode.V_Visible + Mode.V_Total;
+
+ return Mode;
+ end Preferred_Mode;
+
+end HW.GFX.EDID;
diff --git a/common/hw-gfx-edid.ads b/common/hw-gfx-edid.ads
new file mode 100644
index 0000000..28dc2db
--- /dev/null
+++ b/common/hw-gfx-edid.ads
@@ -0,0 +1,79 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.EDID
+is
+
+ use type Word8;
+ use type Word16;
+
+ subtype Raw_EDID_Index is Natural range 0 .. 127;
+ subtype Raw_EDID_Data is Buffer (Raw_EDID_Index);
+
+ function Valid (Raw_EDID : Raw_EDID_Data) return Boolean;
+
+ DESCRIPTOR_1 : constant := 54;
+
+ function Read_LE16
+ (Raw_EDID : Raw_EDID_Data;
+ Offset : Raw_EDID_Index)
+ return Word16
+ with
+ Pre => Offset < Raw_EDID_Index'Last;
+
+ function Has_Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Boolean
+ with
+ Pre => Valid (Raw_EDID),
+ Post =>
+ (Has_Preferred_Mode'Result =
+ (Int64 (Read_LE16 (Raw_EDID, DESCRIPTOR_1)) * 10_000
+ in Frequency_Type and
+ ( Raw_EDID (DESCRIPTOR_1 + 2) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 4) and 16#f0#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 8) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#c0#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 9) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#30#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 3) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 4) and 16#0f#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 5) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 7) and 16#f0#) /= 0) and
+ ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#f0#) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#0c#) /= 0) and
+ ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#0f#) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#03#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 6) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 7) and 16#0f#) /= 0)));
+ function Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Mode_Type
+ with
+ Pre =>
+ Int64 (Read_LE16 (Raw_EDID, DESCRIPTOR_1)) * 10_000
+ in Frequency_Type and
+ ( Raw_EDID (DESCRIPTOR_1 + 2) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 4) and 16#f0#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 8) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#c0#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 9) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#30#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 3) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 4) and 16#0f#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 5) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 7) and 16#f0#) /= 0) and
+ ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#f0#) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#0c#) /= 0) and
+ ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#0f#) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 11) and 16#03#) /= 0) and
+ ( Raw_EDID (DESCRIPTOR_1 + 6) /= 0 or
+ (Raw_EDID (DESCRIPTOR_1 + 7) and 16#0f#) /= 0);
+
+end HW.GFX.EDID;
diff --git a/common/hw-gfx-framebuffer_filler.adb b/common/hw-gfx-framebuffer_filler.adb
new file mode 100644
index 0000000..8c1ffdf
--- /dev/null
+++ b/common/hw-gfx-framebuffer_filler.adb
@@ -0,0 +1,40 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.MMIO_Range;
+pragma Elaborate_All (HW.MMIO_Range);
+
+package body HW.GFX.Framebuffer_Filler
+is
+
+ type FB_Index is new Natural range
+ 0 .. Natural (Width_Type'Last * Height_Type'Last) - 1;
+ type FB_Range is array (FB_Index) of Word32 with Pack;
+ package FB is new MMIO_Range (0, Word32, FB_Index, FB_Range);
+
+ procedure Fill (Linear_FB : Word64; Framebuffer : Framebuffer_Type)
+ is
+ Line_Start : Int32 := 0;
+ begin
+ FB.Set_Base_Address (Linear_FB);
+ for Line in 0 .. Framebuffer.Height - 1 loop
+ pragma Loop_Invariant (Line_Start = Line * Framebuffer.Stride);
+ for Col in 0 .. Framebuffer.Width - 1 loop
+ pragma Loop_Invariant (Line_Start = Line * Framebuffer.Stride);
+ FB.Write (FB_Index (Line_Start + Col), 16#ff000000#);
+ end loop;
+ Line_Start := Line_Start + Framebuffer.Stride;
+ end loop;
+ end Fill;
+
+end HW.GFX.Framebuffer_Filler;
diff --git a/common/hw-gfx-framebuffer_filler.ads b/common/hw-gfx-framebuffer_filler.ads
new file mode 100644
index 0000000..b728a39
--- /dev/null
+++ b/common/hw-gfx-framebuffer_filler.ads
@@ -0,0 +1,28 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 System;
+
+with HW;
+
+use type HW.Int32;
+
+package HW.GFX.Framebuffer_Filler
+is
+
+ procedure Fill (Linear_FB : Word64; Framebuffer : Framebuffer_Type)
+ with
+ Pre =>
+ Framebuffer.Width <= Framebuffer.Stride;
+
+end HW.GFX.Framebuffer_Filler;
diff --git a/common/hw-gfx-gma-config.ads.template b/common/hw-gfx-gma-config.ads.template
new file mode 100644
index 0000000..00418cd
--- /dev/null
+++ b/common/hw-gfx-gma-config.ads.template
@@ -0,0 +1,205 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Config
+with
+ Initializes => Valid_Port_GPU
+is
+
+ CPU : constant CPU_Type := <<CPU>>;
+
+ CPU_Var : constant CPU_Variant := <<CPU_VARIANT>>;
+
+ Internal_Display : constant Internal_Type := <<INTERNAL_PORT>>;
+
+ EDP_Low_Voltage_Swing : constant Boolean := False;
+
+ Default_MMIO_Base : constant := <<DEFAULT_MMIO_BASE>>;
+
+ LVDS_Dual_Threshold : constant := 95_000_000;
+
+ ----------------------------------------------------------------------------
+
+ Has_Internal_Display : constant Boolean := Internal_Display /= None;
+ Internal_Is_EDP : constant Boolean := Internal_Display = DP;
+
+ ----- CPU pipe: --------
+ Disable_Trickle_Feed : constant Boolean := not
+ (CPU in Haswell .. Broadwell);
+ Pipe_Enabled_Workaround : constant Boolean := CPU = Broadwell;
+ Has_EDP_Pipe : constant Boolean := CPU >= Haswell;
+ Has_Pipe_DDI_Func : constant Boolean := CPU >= Haswell;
+ Has_Trans_Clk_Sel : constant Boolean := CPU >= Haswell;
+ Has_Pipe_MSA_Misc : constant Boolean := CPU >= Haswell;
+ Has_Pipeconf_Misc : constant Boolean := CPU >= Broadwell;
+ Has_Pipeconf_BPC : constant Boolean := CPU /= Haswell;
+ Has_Plane_Control : constant Boolean := CPU >= Skylake;
+ Has_DSP_Linoff : constant Boolean := CPU <= Ivybridge;
+
+ ----- Panel power: -----
+ Has_PP_Write_Protection : constant Boolean := CPU <= Ivybridge;
+ Has_PP_Port_Select : constant Boolean := CPU <= Ivybridge;
+ Use_PP_VDD_Override : constant Boolean := CPU <= Ivybridge;
+
+ ----- PCH/FDI: ---------
+ Has_PCH_DAC : constant Boolean := CPU in Ironlake .. Ivybridge or
+ (CPU in Broadwell .. Haswell
+ and CPU_Var = Normal);
+
+ Has_PCH_Aux_Channels : constant Boolean := CPU in Ironlake .. Broadwell;
+
+ VGA_Has_Sync_Disable : constant Boolean := CPU <= Ivybridge;
+
+ Has_Trans_Timing_Ovrrde : constant Boolean := CPU >= Sandybridge;
+
+ Has_DPLL_SEL : constant Boolean := CPU in Ironlake .. Ivybridge;
+ Has_FDI_BPC : constant Boolean := CPU in Ironlake .. Ivybridge;
+ Has_FDI_Composite_Sel : constant Boolean := CPU = Ivybridge;
+ Has_Trans_DP_Ctl : constant Boolean := CPU in
+ Sandybridge .. Ivybridge;
+ Has_FDI_C : constant Boolean := CPU = Ivybridge;
+
+ Has_FDI_RX_Power_Down : constant Boolean := CPU in Haswell .. Broadwell;
+
+ ----- DDI: -------------
+ End_EDP_Training_Late : constant Boolean := CPU in Haswell .. Broadwell;
+ Has_Per_DDI_Clock_Sel : constant Boolean := CPU in Haswell .. Broadwell;
+ Has_HOTPLUG_CTL : constant Boolean := CPU in Haswell .. Broadwell;
+ Has_SHOTPLUG_CTL_A : constant Boolean := (CPU in Haswell .. Broadwell
+ and CPU_Var = ULT) or
+ CPU >= Skylake;
+
+ Has_DDI_D : constant Boolean := (CPU in Haswell .. Broadwell
+ and CPU_Var = Normal)
+ or CPU >= Skylake;
+
+ Has_Low_Voltage_Swing : constant Boolean := CPU >= Skylake;
+
+ Need_DP_Aux_Mutex : constant Boolean := False; -- Skylake & (PSR | GTC)
+
+ Ungate_GMBUS_Unit_Level : constant Boolean := CPU >= Skylake;
+
+ ----- Power: -----------
+ Has_IPS : constant Boolean := (CPU = Haswell and
+ CPU_Var = ULT) or
+ CPU = Broadwell;
+ Has_IPS_CTL_Mailbox : constant Boolean := CPU = Broadwell;
+
+ Has_Per_Pipe_SRD : constant Boolean := CPU >= Broadwell;
+
+ ----- GTT: -----
+ Fold_39Bit_GTT_PTE : constant Boolean := CPU <= Haswell;
+
+ ----------------------------------------------------------------------------
+
+ type Supported_Pipe_Array is array (Config_Index) of Boolean;
+ Supported_Pipe : constant Supported_Pipe_Array :=
+ (Primary => True,
+ Secondary => True,
+ Tertiary => CPU >= Ivybridge);
+
+ type Valid_Per_Port is array (Port_Type) of Boolean;
+ type Valid_Per_GPU is array (CPU_Type) of Valid_Per_Port;
+ Valid_Port_GPU : Valid_Per_GPU :=
+ (Ironlake => Valid_Per_Port'
+ (Disabled => False,
+ Internal => Config.Internal_Display = LVDS,
+ others => True),
+ Sandybridge => Valid_Per_Port'
+ (Disabled => False,
+ Internal => Config.Internal_Display = LVDS,
+ others => True),
+ Ivybridge => Valid_Per_Port'
+ (Disabled => False,
+ Internal => Config.Internal_Display /= None,
+ others => True),
+ Haswell => Valid_Per_Port'
+ (Disabled => False,
+ Internal => Config.Internal_Display = DP,
+ Digital3 => CPU_Var = Normal,
+ DP3 => CPU_Var = Normal,
+ Analog => CPU_Var = Normal,
+ others => True),
+ Broadwell => Valid_Per_Port'
+ (Disabled => False,
+ Internal => Config.Internal_Display = DP,
+ Digital3 => CPU_Var = Normal,
+ DP3 => CPU_Var = Normal,
+ Analog => CPU_Var = Normal,
+ others => True),
+ Skylake => Valid_Per_Port'
+ (Disabled => False,
+ Internal => Config.Internal_Display = DP,
+ Analog => False,
+ others => True))
+ with
+ Part_Of => GMA.Config_State;
+ Valid_Port : Valid_Per_Port renames Valid_Port_GPU (CPU);
+
+ ----------------------------------------------------------------------------
+
+ type FDI_Per_Port is array (GPU_Port) of Boolean;
+ type FDI_Per_GPU is array (CPU_Type) of FDI_Per_Port;
+ FDI_GPU : constant FDI_Per_GPU :=
+ (Ironlake => FDI_Per_Port'
+ (DIGI_A => False, -- directly connected eDP
+ DIGI_B => True,
+ DIGI_C => True,
+ DIGI_D => True,
+ others => False),
+ Sandybridge => FDI_Per_Port'
+ (DIGI_A => False, -- directly connected eDP
+ DIGI_B => True,
+ DIGI_C => True,
+ DIGI_D => True,
+ others => False),
+ Ivybridge => FDI_Per_Port'
+ (DIGI_A => False, -- directly connected eDP
+ DIGI_B => True,
+ DIGI_C => True,
+ DIGI_D => True,
+ others => False),
+ Haswell => FDI_Per_Port'
+ (DIGI_A => False,
+ DIGI_B => False,
+ DIGI_C => False,
+ DIGI_D => False,
+ DIGI_E => True, -- VGA option through FDI
+ others => False),
+ Broadwell => FDI_Per_Port'
+ (DIGI_A => False,
+ DIGI_B => False,
+ DIGI_C => False,
+ DIGI_D => False,
+ DIGI_E => CPU_Var = Normal, -- VGA option through FDI
+ others => False),
+ Skylake => FDI_Per_Port'
+ (others => False));
+ FDI_Port : FDI_Per_Port renames FDI_GPU (CPU);
+
+ type FDI_Lanes_Per_Port is array (GPU_Port) of DP_Lane_Count;
+ FDI_Lane_Count : constant FDI_Lanes_Per_Port :=
+ (DIGI_D => DP_Lane_Count_2,
+ others =>
+ (if CPU in Ironlake .. Ivybridge then
+ DP_Lane_Count_4
+ else
+ DP_Lane_Count_2));
+
+ FDI_Training : constant FDI_Training_Type :=
+ (case CPU is
+ when Ironlake => Simple_Training,
+ when Sandybridge => Full_Training,
+ when others => Auto_Training);
+
+end HW.GFX.GMA.Config;
diff --git a/common/hw-gfx-gma-connector_info.adb b/common/hw-gfx-gma-connector_info.adb
new file mode 100644
index 0000000..bec59fa
--- /dev/null
+++ b/common/hw-gfx-gma-connector_info.adb
@@ -0,0 +1,136 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.I2C;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.I2C;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.DP_Aux_Ch;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connector_Info is
+
+ function To_DP (Port_Cfg : Port_Config) return DP_Port
+ is
+ begin
+ return
+ (if Port_Cfg.Port = DIGI_A then
+ DP_A
+ else
+ (case Port_Cfg.PCH_Port is
+ when PCH_DP_B => DP_B,
+ when PCH_DP_C => DP_C,
+ when PCH_DP_D => DP_D,
+ when others => DP_Port'First));
+ end To_DP;
+
+ ----------------------------------------------------------------------------
+
+ procedure Read_EDID
+ (Raw_EDID : out EDID.Raw_EDID_Data;
+ Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ is
+ Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ for I in 1 .. 2 loop
+ if Port_Cfg.Display = DP then
+ DP_Aux_Ch.I2C_Read
+ (Port => To_DP (Port_Cfg),
+ Address => 16#50#,
+ Length => Raw_EDID_Length,
+ Data => Raw_EDID,
+ Success => Success);
+ else
+ I2C.I2C_Read
+ (Port => Port_Cfg.PCH_Port,
+ Address => 16#50#,
+ Length => Raw_EDID_Length,
+ Data => Raw_EDID,
+ Success => Success);
+ end if;
+ exit when not Success; -- don't retry if reading itself failed
+
+ pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
+ Success := EDID.Valid (Raw_EDID);
+ exit when Success;
+ end loop;
+ end Read_EDID;
+
+ ----------------------------------------------------------------------------
+
+ procedure Preferred_Link_Setting
+ (Port_Cfg : in out Port_Config;
+ Success : out Boolean) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Display = DP then
+ if Port_Cfg.Port = DIGI_A then
+ if GMA.Config.Use_PP_VDD_Override then
+ Panel.VDD_Override;
+ else
+ Panel.On;
+ end if;
+ end if;
+
+ DP_Info.Read_Caps
+ (Link => Port_Cfg.DP,
+ Port => To_DP (Port_Cfg),
+ Success => Success);
+ if Success then
+ DP_Info.Preferred_Link_Setting
+ (Link => Port_Cfg.DP,
+ Mode => Port_Cfg.Mode,
+ Success => Success);
+ end if;
+ else
+ Success := True;
+ end if;
+ end Preferred_Link_Setting;
+
+ procedure Next_Link_Setting
+ (Port_Cfg : in out Port_Config;
+ Success : out Boolean)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Display = DP then
+ DP_Info.Next_Link_Setting
+ (Link => Port_Cfg.DP,
+ Mode => Port_Cfg.Mode,
+ Success => Success);
+ else
+ Success := False;
+ end if;
+ end Next_Link_Setting;
+
+ ----------------------------------------------------------------------------
+
+ function Default_BPC (Port_Cfg : Port_Config) return HW.GFX.BPC_Type
+ is
+ begin
+ return
+ (if Port_Cfg.Port = DIGI_A or
+ (Port_Cfg.Is_FDI and Port_Cfg.PCH_Port = PCH_LVDS)
+ then 6
+ else 8);
+ end Default_BPC;
+
+end HW.GFX.GMA.Connector_Info;
diff --git a/common/hw-gfx-gma-connector_info.ads b/common/hw-gfx-gma-connector_info.ads
new file mode 100644
index 0000000..a02f7c0
--- /dev/null
+++ b/common/hw-gfx-gma-connector_info.ads
@@ -0,0 +1,39 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.EDID;
+
+private package HW.GFX.GMA.Connector_Info is
+
+ procedure Read_EDID
+ (Raw_EDID : out EDID.Raw_EDID_Data;
+ Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ with
+ Post => (if Success then EDID.Valid (Raw_EDID));
+
+ procedure Preferred_Link_Setting
+ (Port_Cfg : in out Port_Config;
+ Success : out Boolean)
+ with
+ Post => (Port_Cfg.Port = Port_Cfg.Port'Old);
+
+ procedure Next_Link_Setting
+ (Port_Cfg : in out Port_Config;
+ Success : out Boolean)
+ with
+ Post => (Port_Cfg.Port = Port_Cfg.Port'Old);
+
+ function Default_BPC (Port_Cfg : Port_Config) return BPC_Type;
+
+end HW.GFX.GMA.Connector_Info;
diff --git a/common/hw-gfx-gma-connectors.ads b/common/hw-gfx-gma-connectors.ads
new file mode 100644
index 0000000..91c2801
--- /dev/null
+++ b/common/hw-gfx-gma-connectors.ads
@@ -0,0 +1,36 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Connectors is
+
+ pragma Warnings (GNATprove, Off, "unused variable ""P*""",
+ Reason => "Needed for a common interface");
+ procedure Pre_On
+ (Port_Cfg : in Port_Config;
+ PLL_Hint : in Word32;
+ Pipe_Hint : in Word32;
+ Success : out Boolean);
+
+ procedure Post_On
+ (Port_Cfg : in Port_Config;
+ PLL_Hint : in Word32;
+ Success : out Boolean);
+ pragma Warnings (GNATprove, On, "unused variable ""P*""");
+
+ procedure Pre_Off (Port_Cfg : Port_Config);
+ procedure Post_Off (Port_Cfg : Port_Config);
+
+ procedure Pre_All_Off;
+ procedure Post_All_Off;
+
+end HW.GFX.GMA.Connectors;
diff --git a/common/hw-gfx-gma-dp_aux_ch.ads b/common/hw-gfx-gma-dp_aux_ch.ads
new file mode 100644
index 0000000..94ef345
--- /dev/null
+++ b/common/hw-gfx-gma-dp_aux_ch.ads
@@ -0,0 +1,21 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.DP_Aux_Ch;
+pragma Elaborate_All (HW.GFX.DP_Aux_Ch);
+with HW.GFX.GMA.DP_Aux_Request;
+
+private package HW.GFX.GMA.DP_Aux_Ch
+ is new HW.GFX.DP_Aux_Ch
+ (T => DP_Port,
+ Aux_Request => DP_Aux_Request.Do_Aux_Request);
diff --git a/common/hw-gfx-gma-dp_aux_request.adb b/common/hw-gfx-gma-dp_aux_request.adb
new file mode 100644
index 0000000..df4e048
--- /dev/null
+++ b/common/hw-gfx-gma-dp_aux_request.adb
@@ -0,0 +1,330 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+use type HW.Word8;
+use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
+
+package body HW.GFX.GMA.DP_Aux_Request is
+
+ DP_AUX_CTL_SEND_BUSY : constant := 1 * 2 ** 31;
+ DP_AUX_CTL_DONE : constant := 1 * 2 ** 30;
+ DP_AUX_CTL_INTERRUPT_ON_DONE : constant := 1 * 2 ** 29;
+ DP_AUX_CTL_TIME_OUT_ERROR : constant := 1 * 2 ** 28;
+ DP_AUX_CTL_TIME_OUT_TIMER_MASK : constant := 3 * 2 ** 26;
+ DP_AUX_CTL_TIME_OUT_TIMER_400US : constant := 0 * 2 ** 26;
+ DP_AUX_CTL_TIME_OUT_TIMER_600US : constant := 1 * 2 ** 26;
+ DP_AUX_CTL_TIME_OUT_TIMER_800US : constant := 2 * 2 ** 26;
+ DP_AUX_CTL_TIME_OUT_TIMER_1600US : constant := 3 * 2 ** 26;
+ DP_AUX_CTL_RECEIVE_ERROR : constant := 1 * 2 ** 25;
+ DP_AUX_CTL_MESSAGE_SIZE_MASK : constant := 31 * 2 ** 20;
+ DP_AUX_CTL_MESSAGE_SIZE_SHIFT : constant := 2 ** 20;
+ DP_AUX_CTL_PRECHARGE_TIME_MASK : constant := 15 * 2 ** 16;
+ DP_AUX_CTL_PRECHARGE_TIME_SHIFT : constant := 2 ** 16;
+ DP_AUX_CTL_2X_BIT_CLOCK_DIV_MASK : constant := 2047 * 2 ** 0;
+ -- TODO: 2x bit clock divider should be programmed once before any training.
+
+ subtype DP_AUX_CTL_MESSAGE_SIZE_T is Natural range 1 .. 20;
+ function DP_AUX_CTL_MESSAGE_SIZE
+ (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
+ return Word32;
+
+ DDI_AUX_MUTEX_MUTEX_ENABLE : constant := 1 * 2 ** 31;
+ DDI_AUX_MUTEX_MUTEX_STATUS : constant := 1 * 2 ** 30;
+
+ type AUX_CH_Data_Regs is new Positive range 1 .. 5;
+
+ type AUX_CH_Data_Regs_Array is
+ array (AUX_CH_Data_Regs) of Registers.Registers_Index;
+
+ type AUX_CH_Registers is record
+ CTL : Registers.Registers_Index;
+ DATA : AUX_CH_Data_Regs_Array;
+ MUTEX : Registers.Registers_Invalid_Index;
+ end record;
+
+ type AUX_CH_Registers_Array is array (DP_Port) of AUX_CH_Registers;
+
+ AUX_CH : constant AUX_CH_Registers_Array :=
+ (if Config.Has_PCH_Aux_Channels then
+ AUX_CH_Registers_Array'
+ (DP_A => AUX_CH_Registers'
+ (CTL => Registers.DP_AUX_CTL_A,
+ DATA => AUX_CH_Data_Regs_Array'
+ (1 => Registers.DP_AUX_DATA_A_1,
+ 2 => Registers.DP_AUX_DATA_A_2,
+ 3 => Registers.DP_AUX_DATA_A_3,
+ 4 => Registers.DP_AUX_DATA_A_4,
+ 5 => Registers.DP_AUX_DATA_A_5),
+ MUTEX => Registers.Invalid_Register),
+ DP_B => AUX_CH_Registers'
+ (CTL => Registers.PCH_DP_AUX_CTL_B,
+ DATA => AUX_CH_Data_Regs_Array'
+ (1 => Registers.PCH_DP_AUX_DATA_B_1,
+ 2 => Registers.PCH_DP_AUX_DATA_B_2,
+ 3 => Registers.PCH_DP_AUX_DATA_B_3,
+ 4 => Registers.PCH_DP_AUX_DATA_B_4,
+ 5 => Registers.PCH_DP_AUX_DATA_B_5),
+ MUTEX => Registers.Invalid_Register),
+ DP_C => AUX_CH_Registers'
+ (CTL => Registers.PCH_DP_AUX_CTL_C,
+ DATA => AUX_CH_Data_Regs_Array'
+ (1 => Registers.PCH_DP_AUX_DATA_C_1,
+ 2 => Registers.PCH_DP_AUX_DATA_C_2,
+ 3 => Registers.PCH_DP_AUX_DATA_C_3,
+ 4 => Registers.PCH_DP_AUX_DATA_C_4,
+ 5 => Registers.PCH_DP_AUX_DATA_C_5),
+ MUTEX => Registers.Invalid_Register),
+ DP_D => AUX_CH_Registers'
+ (CTL => Registers.PCH_DP_AUX_CTL_D,
+ DATA => AUX_CH_Data_Regs_Array'
+ (1 => Registers.PCH_DP_AUX_DATA_D_1,
+ 2 => Registers.PCH_DP_AUX_DATA_D_2,
+ 3 => Registers.PCH_DP_AUX_DATA_D_3,
+ 4 => Registers.PCH_DP_AUX_DATA_D_4,
+ 5 => Registers.PCH_DP_AUX_DATA_D_5),
+ MUTEX => Registers.Invalid_Register))
+ else
+ AUX_CH_Registers_Array'
+ (DP_A => AUX_CH_Registers'
+ (CTL => Registers.DDI_AUX_CTL_A,
+ DATA => AUX_CH_Data_Regs_Array'
+ (1 => Registers.DDI_AUX_DATA_A_1,
+ 2 => Registers.DDI_AUX_DATA_A_2,
+ 3 => Registers.DDI_AUX_DATA_A_3,
+ 4 => Registers.DDI_AUX_DATA_A_4,
+ 5 => Registers.DDI_AUX_DATA_A_5),
+ MUTEX => Registers.DDI_AUX_MUTEX_A),
+ DP_B => AUX_CH_Registers'
+ (CTL => Registers.DDI_AUX_CTL_B,
+ DATA => AUX_CH_Data_Regs_Array'
+ (1 => Registers.DDI_AUX_DATA_B_1,
+ 2 => Registers.DDI_AUX_DATA_B_2,
+ 3 => Registers.DDI_AUX_DATA_B_3,
+ 4 => Registers.DDI_AUX_DATA_B_4,
+ 5 => Registers.DDI_AUX_DATA_B_5),
+ MUTEX => Registers.DDI_AUX_MUTEX_B),
+ DP_C => AUX_CH_Registers'
+ (CTL => Registers.DDI_AUX_CTL_C,
+ DATA => AUX_CH_Data_Regs_Array'
+ (1 => Registers.DDI_AUX_DATA_C_1,
+ 2 => Registers.DDI_AUX_DATA_C_2,
+ 3 => Registers.DDI_AUX_DATA_C_3,
+ 4 => Registers.DDI_AUX_DATA_C_4,
+ 5 => Registers.DDI_AUX_DATA_C_5),
+ MUTEX => Registers.DDI_AUX_MUTEX_C),
+ DP_D => AUX_CH_Registers'
+ (CTL => Registers.DDI_AUX_CTL_D,
+ DATA => AUX_CH_Data_Regs_Array'
+ (1 => Registers.DDI_AUX_DATA_D_1,
+ 2 => Registers.DDI_AUX_DATA_D_2,
+ 3 => Registers.DDI_AUX_DATA_D_3,
+ 4 => Registers.DDI_AUX_DATA_D_4,
+ 5 => Registers.DDI_AUX_DATA_D_5),
+ MUTEX => Registers.DDI_AUX_MUTEX_D)));
+
+ ----------------------------------------------------------------------------
+
+ function DP_AUX_CTL_MESSAGE_SIZE
+ (Message_Length : DP_AUX_CTL_MESSAGE_SIZE_T)
+ return Word32
+ is
+ begin
+ return Word32 (Message_Length) * DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
+ end DP_AUX_CTL_MESSAGE_SIZE;
+
+ ----------------------------------------------------------------------------
+
+ procedure Aux_Request_Low
+ (Port : in DP_Port;
+ Request : in DP_Defs.Aux_Request;
+ Request_Length : in DP_Defs.Aux_Request_Length;
+ Response : out DP_Defs.Aux_Response;
+ Response_Length : out DP_Defs.Aux_Response_Length;
+ Success : out Boolean)
+ with
+ Global => (In_Out => Registers.Register_State,
+ Input => Time.State),
+ Depends =>
+ ((Registers.Register_State,
+ Response,
+ Response_Length,
+ Success)
+ =>
+ (Registers.Register_State,
+ Time.State,
+ Port,
+ Request,
+ Request_Length))
+ is
+ procedure Write_Data_Reg
+ (Register : in Registers.Registers_Index;
+ Buf : in DP_Defs.Aux_Request;
+ Length : in DP_Defs.Aux_Request_Length;
+ Offset : in DP_Defs.Aux_Request_Index)
+ is
+ Value : Word32;
+ Count : Natural;
+ begin
+ if Offset < Length then
+ if Length - Offset > 4 then
+ Count := 4;
+ else
+ Count := Length - Offset;
+ end if;
+
+ Value := 0;
+ for Idx in DP_Defs.Aux_Request_Index range 0 .. Count - 1 loop
+ Value := Value or
+ Shift_Left (Word32 (Buf (Offset + Idx)), (3 - Idx) * 8);
+ end loop;
+ Registers.Write (Register => Register, Value => Value);
+ end if;
+ end Write_Data_Reg;
+
+ procedure Read_Data_Reg
+ (Register : in Registers.Registers_Index;
+ Buf : in out DP_Defs.Aux_Response;
+ Length : in DP_Defs.Aux_Response_Length;
+ Offset : in DP_Defs.Aux_Response_Index)
+ is
+ Value : Word32;
+ Count : DP_Defs.Aux_Response_Length;
+ begin
+ if Offset < Length then
+ if Length - Offset > 4 then
+ Count := 4;
+ else
+ Count := Length - Offset;
+ end if;
+
+ Registers.Read (Register => Register, Value => Value);
+ for Idx in 0 .. Count - 1 loop
+ Buf (Offset + Idx) :=
+ Word8 (Shift_Right (Value, (3 - Idx) * 8) and 16#ff#);
+ end loop;
+ end if;
+ end Read_Data_Reg;
+
+ Busy : Boolean;
+ Status : Word32;
+ begin
+ Response := (others => 0); -- Don't care
+ Response_Length := DP_Defs.Aux_Response_Length'First;
+
+ if Config.Need_DP_Aux_Mutex then
+ Registers.Set_Mask
+ (Register => AUX_CH (Port).MUTEX,
+ Mask => DDI_AUX_MUTEX_MUTEX_ENABLE);
+ Registers.Wait_Set_Mask
+ (Register => AUX_CH (Port).MUTEX,
+ Mask => DDI_AUX_MUTEX_MUTEX_STATUS);
+ end if;
+
+ Registers.Is_Set_Mask
+ (Register => AUX_CH (Port).CTL,
+ Mask => DP_AUX_CTL_SEND_BUSY,
+ Result => Busy);
+ if Busy then
+ Success := False;
+ else
+ for Idx in AUX_CH_Data_Regs loop
+ Write_Data_Reg
+ (Register => AUX_CH (Port).DATA (Idx),
+ Buf => Request,
+ Length => Request_Length,
+ Offset => (Natural (Idx) - 1) * 4);
+ end loop;
+
+ Registers.Unset_And_Set_Mask
+ (Register => AUX_CH (Port).CTL,
+ Mask_Unset => DP_AUX_CTL_INTERRUPT_ON_DONE or
+ DP_AUX_CTL_TIME_OUT_TIMER_MASK or
+ DP_AUX_CTL_MESSAGE_SIZE_MASK,
+ Mask_Set => DP_AUX_CTL_SEND_BUSY or -- starts transfer
+ DP_AUX_CTL_DONE or -- clears the status
+ DP_AUX_CTL_TIME_OUT_ERROR or -- clears the status
+ DP_AUX_CTL_RECEIVE_ERROR or -- clears the status
+ DP_AUX_CTL_TIME_OUT_TIMER_600US or
+ DP_AUX_CTL_MESSAGE_SIZE (Request_Length));
+
+ Registers.Wait_Unset_Mask
+ (Register => AUX_CH (Port).CTL,
+ Mask => DP_AUX_CTL_SEND_BUSY);
+ Registers.Read (Register => AUX_CH (Port).CTL, Value => Status);
+ Success := (Status and
+ (DP_AUX_CTL_TIME_OUT_ERROR or DP_AUX_CTL_RECEIVE_ERROR))
+ = 0;
+
+ if Success then
+ Status := (Status and DP_AUX_CTL_MESSAGE_SIZE_MASK)
+ / DP_AUX_CTL_MESSAGE_SIZE_SHIFT;
+ if Natural (Status) < DP_Defs.Aux_Response_Length'First then
+ Success := False;
+ elsif Natural (Status) > DP_Defs.Aux_Response_Length'Last then
+ Response_Length := DP_Defs.Aux_Response_Length'Last;
+ else
+ Response_Length := Natural (Status);
+ end if;
+ end if;
+
+ if Success then
+ for Idx in AUX_CH_Data_Regs loop
+ Read_Data_Reg
+ (Register => AUX_CH (Port).DATA (Idx),
+ Buf => Response,
+ Length => Response_Length,
+ Offset => (Natural (Idx) - 1) * 4);
+ end loop;
+ end if;
+ end if;
+
+ if Config.Need_DP_Aux_Mutex then
+ Registers.Unset_And_Set_Mask
+ (Register => AUX_CH (Port).MUTEX,
+ Mask_Unset => DDI_AUX_MUTEX_MUTEX_ENABLE,
+ Mask_Set => DDI_AUX_MUTEX_MUTEX_STATUS); -- frees the mutex
+ end if;
+ end Aux_Request_Low;
+
+ ----------------------------------------------------------------------------
+
+ procedure Do_Aux_Request
+ (Port : in DP_Port;
+ Request : in DP_Defs.Aux_Request;
+ Request_Length : in DP_Defs.Aux_Request_Length;
+ Response : out DP_Defs.Aux_Response;
+ Response_Length : out DP_Defs.Aux_Response_Length;
+ Success : out Boolean)
+ is
+ begin
+ for Try in Positive range 1 .. 3 loop
+ Aux_Request_Low
+ (Port => Port,
+ Request => Request,
+ Request_Length => Request_Length,
+ Response => Response,
+ Response_Length => Response_Length,
+ Success => Success);
+ exit when Success;
+ end loop;
+ end Do_Aux_Request;
+
+end HW.GFX.GMA.DP_Aux_Request;
diff --git a/common/hw-gfx-gma-dp_aux_request.ads b/common/hw-gfx-gma-dp_aux_request.ads
new file mode 100644
index 0000000..e3f76ab
--- /dev/null
+++ b/common/hw-gfx-gma-dp_aux_request.ads
@@ -0,0 +1,26 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.DP_Defs;
+
+private package HW.GFX.GMA.DP_Aux_Request is
+
+ procedure Do_Aux_Request
+ (Port : in DP_Port;
+ Request : in DP_Defs.Aux_Request;
+ Request_Length : in DP_Defs.Aux_Request_Length;
+ Response : out DP_Defs.Aux_Response;
+ Response_Length : out DP_Defs.Aux_Response_Length;
+ Success : out Boolean);
+
+end HW.GFX.GMA.DP_Aux_Request;
diff --git a/common/hw-gfx-gma-dp_info.ads b/common/hw-gfx-gma-dp_info.ads
new file mode 100644
index 0000000..148599c
--- /dev/null
+++ b/common/hw-gfx-gma-dp_info.ads
@@ -0,0 +1,21 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.DP_Info;
+pragma Elaborate_All (HW.GFX.DP_Info);
+with HW.GFX.GMA.DP_Aux_Ch;
+
+private package HW.GFX.GMA.DP_Info
+ is new HW.GFX.DP_Info
+ (T => DP_Port,
+ Aux_Ch => DP_Aux_Ch);
diff --git a/common/hw-gfx-gma-i2c.adb b/common/hw-gfx-gma-i2c.adb
new file mode 100644
index 0000000..ec3afc2
--- /dev/null
+++ b/common/hw-gfx-gma-i2c.adb
@@ -0,0 +1,234 @@
+--
+-- Copyright (C) 2015 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; version 2 of the License.
+--
+-- 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 HW.Time;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+use type HW.Word8;
+
+package body HW.GFX.GMA.I2C is
+
+ PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL : constant := 1 * 2 ** 31;
+
+ GMBUS0_GMBUS_RATE_SELECT_MASK : constant := 7 * 2 ** 8;
+ GMBUS0_GMBUS_RATE_SELECT_100KHZ : constant := 0 * 2 ** 8;
+ GMBUS0_GMBUS_RATE_SELECT_50KHZ : constant := 1 * 2 ** 8;
+ GMBUS0_PIN_PAIR_SELECT_MASK : constant := 7 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_NONE : constant := 0 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_DAC : constant := 2 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_LVDS : constant := 3 * 2 ** 0;
+ -- Order is C, B, D: no typo!
+ GMBUS0_PIN_PAIR_SELECT_DIGI_C : constant := 4 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_DIGI_B : constant := 5 * 2 ** 0;
+ GMBUS0_PIN_PAIR_SELECT_DIGI_D : constant := 6 * 2 ** 0;
+
+ GMBUS1_SOFTWARE_CLEAR_INTERRUPT : constant := 1 * 2 ** 31;
+ GMBUS1_SOFTWARE_READY : constant := 1 * 2 ** 30;
+ GMBUS1_ENABLE_TIMEOUT : constant := 1 * 2 ** 29;
+ GMBUS1_BUS_CYCLE_SELECT_MASK : constant := 7 * 2 ** 25;
+ GMBUS1_BUS_CYCLE_STOP : constant := 1 * 2 ** 27;
+ GMBUS1_BUS_CYCLE_INDEX : constant := 1 * 2 ** 26;
+ GMBUS1_BUS_CYCLE_WAIT : constant := 1 * 2 ** 25;
+ GMBUS1_TOTAL_BYTE_COUNT_MASK : constant := 511 * 2 ** 16;
+ GMBUS1_TOTAL_BYTE_COUNT_SHIFT : constant := 16;
+ GMBUS1_8BIT_SLAVE_INDEX_MASK : constant := 255 * 2 ** 8;
+ GMBUS1_8BIT_SLAVE_INDEX_SHIFT : constant := 8;
+ GMBUS1_SLAVE_ADDRESS_MASK : constant := 127 * 2 ** 1;
+ GMBUS1_SLAVE_ADDRESS_SHIFT : constant := 1;
+ GMBUS1_DIRECTION_MASK : constant := 1 * 2 ** 0;
+ GMBUS1_DIRECTION_WRITE : constant := 0 * 2 ** 0;
+ GMBUS1_DIRECTION_READ : constant := 1 * 2 ** 0;
+
+ GMBUS2_INUSE : constant := 1 * 2 ** 15;
+ GMBUS2_HARDWARE_WAIT_PHASE : constant := 1 * 2 ** 14;
+ GMBUS2_SLAVE_STALL_TIMEOUT_ERROR : constant := 1 * 2 ** 13;
+ GMBUS2_GMBUS_INTERRUPT_STATUS : constant := 1 * 2 ** 12;
+ GMBUS2_HARDWARE_READY : constant := 1 * 2 ** 11;
+ GMBUS2_NAK_INDICATOR : constant := 1 * 2 ** 10;
+ GMBUS2_GMBUS_ACTIVE : constant := 1 * 2 ** 9;
+ GMBUS2_CURRENT_BYTE_COUNT_MASK : constant := 511 * 2 ** 0;
+
+ GMBUS4_INTERRUPT_MASK : constant := 31 * 2 ** 0;
+
+ GMBUS5_2BYTE_INDEX_ENABLE : constant := 1 * 2 ** 31;
+
+ function GMBUS1_TOTAL_BYTE_COUNT
+ (Count : HW.GFX.I2C.Transfer_Length)
+ return Word32 is
+ begin
+ return Shift_Left (Word32 (Count), GMBUS1_TOTAL_BYTE_COUNT_SHIFT);
+ end GMBUS1_TOTAL_BYTE_COUNT;
+
+ function GMBUS1_SLAVE_ADDRESS
+ (Address : HW.GFX.I2C.Transfer_Address)
+ return Word32 is
+ begin
+ return Shift_Left (Word32 (Address), GMBUS1_SLAVE_ADDRESS_SHIFT);
+ end GMBUS1_SLAVE_ADDRESS;
+
+ function GMBUS0_PIN_PAIR_SELECT (Port : PCH_Port) return Word32 is
+ begin
+ return
+ (case Port is
+ when PCH_DAC => GMBUS0_PIN_PAIR_SELECT_DAC,
+ when PCH_LVDS => GMBUS0_PIN_PAIR_SELECT_LVDS,
+ when PCH_HDMI_B => GMBUS0_PIN_PAIR_SELECT_DIGI_B,
+ when PCH_HDMI_C => GMBUS0_PIN_PAIR_SELECT_DIGI_C,
+ when PCH_HDMI_D => GMBUS0_PIN_PAIR_SELECT_DIGI_D,
+ when others => GMBUS0_PIN_PAIR_SELECT_NONE);
+ end GMBUS0_PIN_PAIR_SELECT;
+
+ ----------------------------------------------------------------------------
+
+ procedure GMBUS_Ready (Result : out Boolean)
+ is
+ GMBUS2 : Word32;
+ begin
+ Registers.Read (Registers.PCH_GMBUS2, GMBUS2);
+ Result := (GMBUS2 and (GMBUS2_HARDWARE_WAIT_PHASE or
+ GMBUS2_SLAVE_STALL_TIMEOUT_ERROR or
+ GMBUS2_GMBUS_INTERRUPT_STATUS or
+ GMBUS2_NAK_INDICATOR)) = 0;
+ end GMBUS_Ready;
+
+ procedure Reset_GMBUS (Success : out Boolean) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Write (Registers.PCH_GMBUS1, GMBUS1_SOFTWARE_CLEAR_INTERRUPT);
+ Registers.Write (Registers.PCH_GMBUS1, 0);
+ Registers.Write (Registers.PCH_GMBUS0, GMBUS0_PIN_PAIR_SELECT_NONE);
+
+ GMBUS_Ready (Success);
+ end Reset_GMBUS;
+
+ procedure Init_GMBUS (Port : PCH_Port; Success : out Boolean) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.Ungate_GMBUS_Unit_Level then
+ Registers.Set_Mask
+ (Register => Registers.PCH_DSPCLK_GATE_D,
+ Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
+ end if;
+
+ -- TODO: Refactor + check for timeout.
+ Registers.Wait_Unset_Mask (Registers.PCH_GMBUS2, GMBUS2_INUSE);
+
+ GMBUS_Ready (Success);
+ if not Success then
+ Reset_GMBUS (Success);
+ end if;
+
+ if Success then
+ Registers.Write
+ (Register => Registers.PCH_GMBUS0,
+ Value => GMBUS0_GMBUS_RATE_SELECT_100KHZ or
+ GMBUS0_PIN_PAIR_SELECT (Port));
+ Registers.Write
+ (Register => Registers.PCH_GMBUS4,
+ Value => 0);
+ Registers.Write
+ (Register => Registers.PCH_GMBUS5,
+ Value => 0);
+ end if;
+ end Init_GMBUS;
+
+ procedure Release_GMBUS
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Write (Registers.PCH_GMBUS0, GMBUS0_PIN_PAIR_SELECT_NONE);
+
+ -- Clear INUSE. TODO: Don't do it, if timeout occured (see above).
+ Registers.Write (Registers.PCH_GMBUS2, GMBUS2_INUSE);
+
+ if Config.Ungate_GMBUS_Unit_Level then
+ Registers.Unset_Mask
+ (Register => Registers.PCH_DSPCLK_GATE_D,
+ Mask => PCH_DSPCLK_GATE_D_GMBUS_UNIT_LVL);
+ end if;
+ end Release_GMBUS;
+
+ procedure I2C_Read
+ (Port : in PCH_Port;
+ Address : in HW.GFX.I2C.Transfer_Address;
+ Length : in out HW.GFX.I2C.Transfer_Length;
+ Data : out HW.GFX.I2C.Transfer_Data;
+ Success : out Boolean)
+ is
+ GMBUS2,
+ GMBUS3 : Word32;
+
+ Current : HW.GFX.I2C.Transfer_Length;
+ Transfered : HW.GFX.I2C.Transfer_Length := 0;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Data := (others => 0);
+
+ Init_GMBUS (Port, Success);
+ if Success then
+ Registers.Write
+ (Register => Registers.PCH_GMBUS1,
+ Value => GMBUS1_SOFTWARE_READY or
+ GMBUS1_BUS_CYCLE_INDEX or
+ GMBUS1_BUS_CYCLE_WAIT or
+ GMBUS1_TOTAL_BYTE_COUNT (Length) or
+ GMBUS1_SLAVE_ADDRESS (Address) or
+ GMBUS1_DIRECTION_READ);
+
+ while Success and then Transfered < Length loop
+ Registers.Wait_Set_Mask
+ (Register => Registers.PCH_GMBUS2,
+ Mask => GMBUS2_HARDWARE_READY,
+ TOut_MS => 55);
+
+ Registers.Read (Registers.PCH_GMBUS2, GMBUS2);
+ Success := (GMBUS2 and GMBUS2_HARDWARE_READY) /= 0 and
+ (GMBUS2 and GMBUS2_NAK_INDICATOR) = 0;
+ if Success then
+ Current := GFX.I2C.Transfer_Length'Min (Length, Transfered + 4);
+
+ Registers.Read (Registers.PCH_GMBUS3, GMBUS3);
+ for I in Transfered .. Current - 1 loop
+ Data (I) := Byte (GMBUS3 and 16#ff#);
+ GMBUS3 := Shift_Right (GMBUS3, 8);
+ end loop;
+ Transfered := Current;
+ end if;
+ end loop;
+ if Success then
+ Registers.Wait_Set_Mask
+ (Register => Registers.PCH_GMBUS2,
+ Mask => GMBUS2_HARDWARE_WAIT_PHASE);
+ Registers.Write
+ (Register => Registers.PCH_GMBUS1,
+ Value => GMBUS1_SOFTWARE_READY or GMBUS1_BUS_CYCLE_STOP);
+ Registers.Wait_Unset_Mask
+ (Register => Registers.PCH_GMBUS2,
+ Mask => GMBUS2_GMBUS_ACTIVE);
+ end if;
+ end if;
+ Length := Transfered;
+
+ Release_GMBUS;
+ end I2C_Read;
+
+end HW.GFX.GMA.I2C;
diff --git a/common/hw-gfx-gma-i2c.ads b/common/hw-gfx-gma-i2c.ads
new file mode 100644
index 0000000..85f90dc
--- /dev/null
+++ b/common/hw-gfx-gma-i2c.ads
@@ -0,0 +1,25 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.I2C;
+
+private package HW.GFX.GMA.I2C is
+
+ procedure I2C_Read
+ (Port : in PCH_Port;
+ Address : in HW.GFX.I2C.Transfer_Address;
+ Length : in out HW.GFX.I2C.Transfer_Length;
+ Data : out HW.GFX.I2C.Transfer_Data;
+ Success : out Boolean);
+
+end HW.GFX.GMA.I2C;
diff --git a/common/hw-gfx-gma-panel.adb b/common/hw-gfx-gma-panel.adb
new file mode 100644
index 0000000..1fc043d
--- /dev/null
+++ b/common/hw-gfx-gma-panel.adb
@@ -0,0 +1,358 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Config;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Panel
+with
+ Refined_State =>
+ (Panel_State =>
+ (Delays_US, Power_Cycle_Timer, Power_Up_Timer))
+is
+ type Delays_Enum is
+ (Power_Up_Delay,
+ Power_Up_To_BL_On,
+ Power_Down_Delay,
+ BL_Off_To_Power_Down,
+ Power_Cycle_Delay);
+
+ type Panel_Power_Delays is array (Delays_Enum) of Natural;
+ Default_EDP_Delays_US : constant Panel_Power_Delays := Panel_Power_Delays'
+ (Power_Up_Delay => 210_000,
+ Power_Up_To_BL_On => 50_000,
+ Power_Down_Delay => 500_000,
+ BL_Off_To_Power_Down => 50_000,
+ Power_Cycle_Delay => 510_000);
+
+ Delays_US : Panel_Power_Delays;
+
+ ----------------------------------------------------------------------------
+
+ -- And here the mess starts: We have this pretty hardware power sequencer
+ -- that should ensure the panel's timing constraints are satisfied. But
+ -- (at least on some generations) it doesn't do it's job. On Haswell, it
+ -- seems to ignore the Power_Cycle_Delay, so we ensure the delay in soft-
+ -- ware. On at least Ivy Bridge and Broadwell Power_Up_Delay is ignored.
+ --
+ -- If we ever do all delays in software, there are two ways: Either confi-
+ -- gure the hardware to zero delays or wait for both the software timeout
+ -- and the hardware power sequencer. The latter option would be less error
+ -- prone, as the hardware might just don't work as expected.
+
+ Power_Cycle_Timer : Time.T;
+ Power_Up_Timer : Time.T;
+
+ ----------------------------------------------------------------------------
+
+ function Div_Round_Up32 (Numerator, Denominator : Natural) return Word32 is
+ begin
+ return (Word32 (Numerator) + Word32 (Denominator) - 1)
+ / Word32 (Denominator);
+ end Div_Round_Up32;
+
+ PCH_PP_STATUS_ENABLED : constant := 16#00_0001# * 2 ** 31;
+ PCH_PP_STATUS_REQUIRE_ASSET : constant := 16#00_0001# * 2 ** 30;
+ PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK : constant := 16#00_0003# * 2 ** 28;
+ PCH_PP_STATUS_PWR_SEQ_PROGRESS_NONE : constant := 16#00_0000# * 2 ** 28;
+ PCH_PP_STATUS_PWR_SEQ_PROGRESS_UP : constant := 16#00_0001# * 2 ** 28;
+ PCH_PP_STATUS_PWR_SEQ_PROGRESS_DOWN : constant := 16#00_0002# * 2 ** 28;
+ PCH_PP_STATUS_PWR_CYC_DELAY_ACTIVE : constant := 16#00_0001# * 2 ** 27;
+
+ PCH_PP_CONTROL_WRITE_PROTECT_MASK : constant := 16#00_ffff# * 2 ** 16;
+ PCH_PP_CONTROL_WRITE_PROTECT_KEY : constant := 16#00_abcd# * 2 ** 16;
+ PCH_PP_CONTROL_VDD_OVERRIDE : constant := 16#00_0001# * 2 ** 3;
+ PCH_PP_CONTROL_BACKLIGHT_ENABLE : constant := 16#00_0001# * 2 ** 2;
+ PCH_PP_CONTROL_POWER_DOWN_ON_RESET : constant := 16#00_0001# * 2 ** 1;
+ PCH_PP_CONTROL_TARGET_ON : constant := 16#00_0001# * 2 ** 0;
+
+ PCH_PP_ON_DELAYS_PORT_SELECT_MASK : constant := 16#00_0003# * 2 ** 30;
+ PCH_PP_ON_DELAYS_PORT_SELECT_LVDS : constant := 16#00_0000# * 2 ** 30;
+ PCH_PP_ON_DELAYS_PORT_SELECT_DP_A : constant := 16#00_0001# * 2 ** 30;
+ PCH_PP_ON_DELAYS_PORT_SELECT_DP_C : constant := 16#00_0002# * 2 ** 30;
+ PCH_PP_ON_DELAYS_PORT_SELECT_DP_D : constant := 16#00_0003# * 2 ** 30;
+ PCH_PP_ON_DELAYS_PWR_UP_MASK : constant := 16#00_1fff# * 2 ** 16;
+ PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK : constant := 16#00_1fff# * 2 ** 0;
+ function PCH_PP_ON_DELAYS_PWR_UP (US : Natural) return Word32 is
+ begin
+ return Shift_Left (Div_Round_Up32 (US, 100), 16);
+ end PCH_PP_ON_DELAYS_PWR_UP;
+ function PCH_PP_ON_DELAYS_PWR_UP_BL_ON (US : Natural) return Word32 is
+ begin
+ return Div_Round_Up32 (US, 100);
+ end PCH_PP_ON_DELAYS_PWR_UP_BL_ON;
+
+ PCH_PP_OFF_DELAYS_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 16;
+ PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK : constant := 16#1fff# * 2 ** 0;
+ function PCH_PP_OFF_DELAYS_PWR_DOWN (US : Natural) return Word32 is
+ begin
+ return Shift_Left (Div_Round_Up32 (US, 100), 16);
+ end PCH_PP_OFF_DELAYS_PWR_DOWN;
+ function PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN (US : Natural) return Word32 is
+ begin
+ return Div_Round_Up32 (US, 100);
+ end PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN;
+
+ PCH_PP_DIVISOR_REF_DIVIDER_MASK : constant := 16#ff_ffff# * 2 ** 8;
+ PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK : constant := 16#00_001f# * 2 ** 0;
+ function PCH_PP_DIVISOR_PWR_CYC_DELAY (US : Natural) return Word32 is
+ begin
+ return Div_Round_Up32 (US, 100_000) + 1;
+ end PCH_PP_DIVISOR_PWR_CYC_DELAY;
+
+ CPU_BLC_PWM_CTL_ENABLE : constant := 16#00_0001# * 2 ** 31;
+ CPU_BLC_PWM_CTL_PIPE_SELECT_MASK : constant := 16#00_0003# * 2 ** 29;
+ CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_A : constant := 16#00_0000# * 2 ** 29;
+ CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_B : constant := 16#00_0001# * 2 ** 29;
+ CPU_BLC_PWM_CTL_PIPE_SELECT_PIPE_C : constant := 16#00_0002# * 2 ** 29;
+
+ CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
+
+ PCH_BLC_PWM_CTL1_ENABLE : constant := 16#00_0001# * 2 ** 31;
+ PCH_BLC_PWM_CTL1_BL_POLARITY_MASK : constant := 16#00_0001# * 2 ** 29;
+ PCH_BLC_PWM_CTL1_PHASE_IN_INTR_STAT : constant := 16#00_0001# * 2 ** 26;
+ PCH_BLC_PWM_CTL1_PHASE_IN_ENABLE : constant := 16#00_0001# * 2 ** 25;
+ PCH_BLC_PWM_CTL1_PHASE_IN_INTR_EN : constant := 16#00_0001# * 2 ** 24;
+ PCH_BLC_PWM_CTL1_PHASE_IN_TIME_BASE : constant := 16#00_00ff# * 2 ** 16;
+ PCH_BLC_PWM_CTL1_PHASE_IN_COUNT : constant := 16#00_00ff# * 2 ** 8;
+ PCH_BLC_PWM_CTL1_PHASE_IN_INCREMENT : constant := 16#00_00ff# * 2 ** 0;
+
+ PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK : constant := 16#00_ffff# * 2 ** 16;
+ PCH_BLC_PWM_CTL2_BL_DUTY_CYC_MASK : constant := 16#00_ffff# * 2 ** 0;
+
+ ----------------------------------------------------------------------------
+
+ procedure Static_Init
+ with
+ Refined_Global =>
+ (Output => (Power_Cycle_Timer, Power_Up_Timer, Delays_US),
+ Input => (Time.State))
+ is
+ begin
+ Power_Cycle_Timer := Time.Now;
+ Power_Up_Timer := Power_Cycle_Timer;
+
+ Delays_US := Default_EDP_Delays_US;
+ end Static_Init;
+
+ ----------------------------------------------------------------------------
+
+ procedure Check_PP_Delays
+ (Delays : in out Panel_Power_Delays;
+ Override : in out Boolean) is
+ begin
+ for D in Delays_Enum loop
+ if Delays (D) = 0 then
+ Delays (D) := Default_EDP_Delays_US (D);
+ Override := True;
+ end if;
+ end loop;
+ end Check_PP_Delays;
+
+ procedure Setup_PP_Sequencer (Default_Delays : Boolean := False)
+ is
+ Power_Delay, Port_Select : Word32;
+
+ Override_Delays : Boolean := False;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Static_Init;
+
+ if Default_Delays then
+ Override_Delays := True;
+ else
+ Registers.Read (Registers.PCH_PP_ON_DELAYS, Power_Delay);
+ Delays_US (Power_Up_Delay) := 100 * Natural
+ (Shift_Right (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_MASK, 16));
+ Delays_US (Power_Up_To_BL_On) := 100 * Natural
+ (Power_Delay and PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK);
+
+ Registers.Read (Registers.PCH_PP_OFF_DELAYS, Power_Delay);
+ Delays_US (Power_Down_Delay) := 100 * Natural
+ (Shift_Right (Power_Delay and PCH_PP_OFF_DELAYS_PWR_DOWN_MASK, 16));
+ Delays_US (BL_Off_To_Power_Down) := 100 * Natural
+ (Power_Delay and PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK);
+
+ Registers.Read (Registers.PCH_PP_DIVISOR, Power_Delay);
+ if (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) > 1 then
+ Delays_US (Power_Cycle_Delay) := 100_000 * (Natural
+ (Power_Delay and PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK) - 1);
+ end if;
+
+ Check_PP_Delays (Delays_US, Override_Delays);
+ end if;
+
+ if Override_Delays then
+ if Config.Has_PP_Port_Select then
+ if Config.Internal_Is_EDP then
+ Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_DP_A;
+ else
+ Port_Select := PCH_PP_ON_DELAYS_PORT_SELECT_LVDS;
+ end if;
+ else
+ Port_Select := 0;
+ end if;
+
+ -- Force power-up to backlight-on delay to 100us as recommended by PRM.
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.PCH_PP_ON_DELAYS,
+ Mask_Unset => PCH_PP_ON_DELAYS_PORT_SELECT_MASK or
+ PCH_PP_ON_DELAYS_PWR_UP_MASK or
+ PCH_PP_ON_DELAYS_PWR_UP_BL_ON_MASK,
+ Mask_Set => Port_Select or
+ PCH_PP_ON_DELAYS_PWR_UP (Delays_US (Power_Up_Delay))
+ or PCH_PP_ON_DELAYS_PWR_UP_BL_ON (100));
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.PCH_PP_OFF_DELAYS,
+ Mask_Unset => PCH_PP_OFF_DELAYS_PWR_DOWN_MASK or
+ PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN_MASK,
+ Mask_Set => PCH_PP_OFF_DELAYS_PWR_DOWN
+ (Delays_US (Power_Down_Delay)) or
+ PCH_PP_OFF_DELAYS_BL_OFF_PWR_DOWN
+ (Delays_US (BL_Off_To_Power_Down)));
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.PCH_PP_DIVISOR,
+ Mask_Unset => PCH_PP_DIVISOR_PWR_CYC_DELAY_MASK,
+ Mask_Set => PCH_PP_DIVISOR_PWR_CYC_DELAY
+ (Delays_US (Power_Cycle_Delay)));
+ end if;
+
+ if Config.Has_PP_Write_Protection then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.PCH_PP_CONTROL,
+ Mask_Unset => PCH_PP_CONTROL_WRITE_PROTECT_MASK,
+ Mask_Set => PCH_PP_CONTROL_WRITE_PROTECT_KEY or
+ PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
+ else
+ Registers.Set_Mask
+ (Register => Registers.PCH_PP_CONTROL,
+ Mask => PCH_PP_CONTROL_POWER_DOWN_ON_RESET);
+ end if;
+ end Setup_PP_Sequencer;
+
+ ----------------------------------------------------------------------------
+
+ procedure VDD_Override is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ -- Yeah, We could do, what we are supposed to do here. But OTOH, we
+ -- are should wait for the full Power Up Delay, which we would have
+ -- to do later again. And just powering on the display seems to work
+ -- too. Also this function vanished on newer hardware.
+ On;
+ end VDD_Override;
+
+ procedure On (Wait : Boolean := True)
+ is
+ Was_On : Boolean;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Is_Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
+ if not Was_On then
+ Time.Delay_Until (Power_Cycle_Timer);
+ end if;
+
+ Registers.Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON);
+ if not Was_On then
+ Power_Up_Timer := Time.US_From_Now (Delays_US (Power_Up_Delay));
+ end if;
+ if Wait then
+ Wait_On;
+ end if;
+ end On;
+
+ procedure Wait_On is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Time.Delay_Until (Power_Up_Timer);
+ Registers.Wait_Unset_Mask
+ (Register => Registers.PCH_PP_STATUS,
+ Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
+ TOut_MS => 300);
+
+ Registers.Unset_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_VDD_OVERRIDE);
+ end Wait_On;
+
+ procedure Off
+ is
+ Was_On : Boolean;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Is_Set_Mask (Registers.PCH_PP_CONTROL, PCH_PP_CONTROL_TARGET_ON, Was_On);
+ Registers.Unset_Mask
+ (Register => Registers.PCH_PP_CONTROL,
+ Mask => PCH_PP_CONTROL_TARGET_ON or
+ PCH_PP_CONTROL_VDD_OVERRIDE);
+ if Was_On then
+ Time.U_Delay (Delays_US (Power_Down_Delay));
+ end if;
+ Registers.Wait_Unset_Mask
+ (Register => Registers.PCH_PP_STATUS,
+ Mask => PCH_PP_STATUS_PWR_SEQ_PROGRESS_MASK,
+ TOut_MS => 600);
+ if Was_On then
+ Power_Cycle_Timer := Time.US_From_Now (Delays_US (Power_Cycle_Delay));
+ end if;
+ end Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure Backlight_On is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Set_Mask
+ (Register => Registers.PCH_PP_CONTROL,
+ Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
+ end Backlight_On;
+
+ procedure Backlight_Off is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_Mask
+ (Register => Registers.PCH_PP_CONTROL,
+ Mask => PCH_PP_CONTROL_BACKLIGHT_ENABLE);
+ end Backlight_Off;
+
+ procedure Set_Backlight (Level : Word16) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.BLC_PWM_CPU_CTL,
+ Mask_Unset => CPU_BLC_PWM_DATA_BL_DUTY_CYC_MASK,
+ Mask_Set => Word32 (Level));
+ end Set_Backlight;
+
+ procedure Get_Max_Backlight (Level : out Word16)
+ is
+ Reg : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Read (Registers.BLC_PWM_PCH_CTL2, Reg);
+ Level := Word16
+ (Shift_Right (Reg and PCH_BLC_PWM_CTL2_BL_MOD_FREQ_MASK, 16));
+ end Get_Max_Backlight;
+
+end HW.GFX.GMA.Panel;
diff --git a/common/hw-gfx-gma-panel.ads b/common/hw-gfx-gma-panel.ads
new file mode 100644
index 0000000..98dc93f
--- /dev/null
+++ b/common/hw-gfx-gma-panel.ads
@@ -0,0 +1,60 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.GMA.Registers;
+
+private package HW.GFX.GMA.Panel
+with
+ Abstract_State => (Panel_State with Part_Of => GMA.State)
+is
+
+ procedure Static_Init
+ with
+ Global =>
+ (Output => Panel_State,
+ Input => Time.State);
+
+ procedure Setup_PP_Sequencer (Default_Delays : Boolean := False)
+ with
+ Global =>
+ (Input => Time.State,
+ In_Out => Registers.Register_State,
+ Output => Panel_State),
+ Depends =>
+ ((Panel_State, Registers.Register_State) =>
+ (Time.State, Registers.Register_State, Default_Delays)),
+ Pre => True,
+ Post => True;
+
+ ----------------------------------------------------------------------------
+
+ procedure VDD_Override;
+
+ procedure On (Wait : Boolean := True);
+
+ procedure Wait_On;
+
+ procedure Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure Backlight_On;
+
+ procedure Backlight_Off;
+
+ procedure Set_Backlight (Level : Word16);
+
+ procedure Get_Max_Backlight (Level : out Word16);
+
+end HW.GFX.GMA.Panel;
diff --git a/common/hw-gfx-gma-pch-fdi.adb b/common/hw-gfx-gma-pch-fdi.adb
new file mode 100644
index 0000000..7b7b3dd
--- /dev/null
+++ b/common/hw-gfx-gma-pch-fdi.adb
@@ -0,0 +1,293 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PCH.FDI is
+
+ FDI_RX_CTL_FDI_RX_ENABLE : constant := 1 * 2 ** 31;
+ FDI_RX_CTL_FS_ERROR_CORRECTION_ENABLE : constant := 1 * 2 ** 27;
+ FDI_RX_CTL_FE_ERROR_CORRECTION_ENABLE : constant := 1 * 2 ** 26;
+ FDI_RX_CTL_PORT_WIDTH_SEL_SHIFT : constant := 19;
+ FDI_RX_CTL_FDI_PLL_ENABLE : constant := 1 * 2 ** 13;
+ FDI_RX_CTL_COMPOSITE_SYNC_SELECT : constant := 1 * 2 ** 11;
+ FDI_RX_CTL_FDI_AUTO_TRAIN : constant := 1 * 2 ** 10;
+ FDI_RX_CTL_ENHANCED_FRAMING_ENABLE : constant := 1 * 2 ** 6;
+ FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_MASK : constant := 1 * 2 ** 4;
+ FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_RAWCLK : constant := 0 * 2 ** 4;
+ FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_PCDCLK : constant := 1 * 2 ** 4;
+
+ TP_SHIFT : constant := (if Config.CPU = Ironlake then 28 else 8);
+ FDI_RX_CTL_TRAINING_PATTERN_MASK : constant := 3 * 2 ** TP_SHIFT;
+
+ type TP_Array is array (Training_Pattern) of Word32;
+ FDI_RX_CTL_TRAINING_PATTERN : constant TP_Array :=
+ (TP_1 => 0 * 2 ** TP_SHIFT,
+ TP_2 => 1 * 2 ** TP_SHIFT,
+ TP_Idle => 2 * 2 ** TP_SHIFT,
+ TP_None => 3 * 2 ** TP_SHIFT);
+
+ function FDI_RX_CTL_PORT_WIDTH_SEL (Lane_Count : DP_Lane_Count) return Word32
+ is
+ begin
+ return Shift_Left
+ (Word32 (Lane_Count_As_Integer (Lane_Count)) - 1,
+ FDI_RX_CTL_PORT_WIDTH_SEL_SHIFT);
+ end FDI_RX_CTL_PORT_WIDTH_SEL;
+
+ function FDI_RX_CTL_BPC (BPC : BPC_Type) return Word32
+ with Pre => True
+ is
+ begin
+ return
+ (case BPC is
+ when 6 => 2 * 2 ** 16,
+ when 10 => 1 * 2 ** 16,
+ when 12 => 3 * 2 ** 16,
+ when others => 0 * 2 ** 16);
+ end FDI_RX_CTL_BPC;
+
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE1_SHIFT : constant := 26;
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE1_MASK : constant := 3 * 2 ** 26;
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE0_SHIFT : constant := 24;
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE0_MASK : constant := 3 * 2 ** 24;
+ FDI_RX_MISC_TP1_TO_TP2_TIME_48 : constant := 2 * 2 ** 20;
+ FDI_RX_MISC_FDI_DELAY_90 : constant := 16#90# * 2 ** 0;
+
+ function FDI_RX_MISC_FDI_RX_PWRDN_LANE1 (Value : Word32) return Word32
+ with Pre => True
+ is
+ begin
+ return Shift_Left (Value, FDI_RX_MISC_FDI_RX_PWRDN_LANE1_SHIFT);
+ end FDI_RX_MISC_FDI_RX_PWRDN_LANE1;
+
+ function FDI_RX_MISC_FDI_RX_PWRDN_LANE0 (Value : Word32) return Word32
+ with Pre => True
+ is
+ begin
+ return Shift_Left (Value, FDI_RX_MISC_FDI_RX_PWRDN_LANE0_SHIFT);
+ end FDI_RX_MISC_FDI_RX_PWRDN_LANE0;
+
+ FDI_RX_TUSIZE_SHIFT : constant := 25;
+
+ function FDI_RX_TUSIZE (Value : Word32) return Word32 is
+ begin
+ return Shift_Left (Value - 1, FDI_RX_TUSIZE_SHIFT);
+ end FDI_RX_TUSIZE;
+
+ FDI_RX_INTERLANE_ALIGNMENT : constant := 1 * 2 ** 10;
+ FDI_RX_SYMBOL_LOCK : constant := 1 * 2 ** 9;
+ FDI_RX_BIT_LOCK : constant := 1 * 2 ** 8;
+
+ ----------------------------------------------------------------------------
+
+ type FDI_Registers is record
+ RX_CTL : Registers.Registers_Index;
+ RX_MISC : Registers.Registers_Index;
+ RX_TUSIZE : Registers.Registers_Index;
+ RX_IMR : Registers.Registers_Index;
+ RX_IIR : Registers.Registers_Index;
+ end record;
+ type FDI_Registers_Array is array (PCH.FDI_Port_Type) of FDI_Registers;
+ FDI_Regs : constant FDI_Registers_Array := FDI_Registers_Array'
+ (PCH.FDI_A => FDI_Registers'
+ (RX_CTL => Registers.FDI_RXA_CTL,
+ RX_MISC => Registers.FDI_RX_MISC_A,
+ RX_TUSIZE => Registers.FDI_RXA_TUSIZE1,
+ RX_IMR => Registers.FDI_RXA_IMR,
+ RX_IIR => Registers.FDI_RXA_IIR),
+ PCH.FDI_B => FDI_Registers'
+ (RX_CTL => Registers.FDI_RXB_CTL,
+ RX_MISC => Registers.FDI_RX_MISC_B,
+ RX_TUSIZE => Registers.FDI_RXB_TUSIZE1,
+ RX_IMR => Registers.FDI_RXB_IMR,
+ RX_IIR => Registers.FDI_RXB_IIR),
+ PCH.FDI_C => FDI_Registers'
+ (RX_CTL => Registers.FDI_RXC_CTL,
+ RX_MISC => Registers.FDI_RX_MISC_C,
+ RX_TUSIZE => Registers.FDI_RXC_TUSIZE1,
+ RX_IMR => Registers.FDI_RXC_IMR,
+ RX_IIR => Registers.FDI_RXC_IIR));
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_Train (Port : PCH.FDI_Port_Type; Port_Cfg : Port_Config)
+ is
+ Power_Down_Lane_Bits : constant Word32 :=
+ (if Config.Has_FDI_RX_Power_Down then
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE1 (2) or
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE0 (2)
+ else 0);
+ RX_CTL_Settings : constant Word32 :=
+ FDI_RX_CTL_PORT_WIDTH_SEL (Port_Cfg.FDI.Lane_Count) or
+ (if Config.Has_FDI_BPC then
+ FDI_RX_CTL_BPC (Port_Cfg.Mode.BPC) else 0) or
+ (if Config.Has_FDI_Composite_Sel then
+ FDI_RX_CTL_COMPOSITE_SYNC_SELECT else 0) or
+ (if Port_Cfg.FDI.Enhanced_Framing then
+ FDI_RX_CTL_ENHANCED_FRAMING_ENABLE else 0);
+ begin
+ -- TODO: HSW: check DISPIO_CR_TX_BMU_CR4, seems Linux doesn't know it
+
+ Registers.Write
+ (Register => FDI_Regs (Port).RX_MISC,
+ Value => Power_Down_Lane_Bits or
+ FDI_RX_MISC_TP1_TO_TP2_TIME_48 or
+ FDI_RX_MISC_FDI_DELAY_90);
+
+ Registers.Write
+ (Register => FDI_Regs (Port).RX_TUSIZE,
+ Value => FDI_RX_TUSIZE (64));
+
+ Registers.Unset_Mask
+ (Register => FDI_Regs (Port).RX_IMR,
+ Mask => FDI_RX_INTERLANE_ALIGNMENT or
+ FDI_RX_SYMBOL_LOCK or
+ FDI_RX_BIT_LOCK);
+ Registers.Posting_Read (FDI_Regs (Port).RX_IMR);
+ -- clear stale lock bits
+ Registers.Write
+ (Register => FDI_Regs (Port).RX_IIR,
+ Value => FDI_RX_INTERLANE_ALIGNMENT or
+ FDI_RX_SYMBOL_LOCK or
+ FDI_RX_BIT_LOCK);
+
+ Registers.Write
+ (Register => FDI_Regs (Port).RX_CTL,
+ Value => FDI_RX_CTL_FDI_PLL_ENABLE or
+ RX_CTL_Settings);
+ Registers.Posting_Read (FDI_Regs (Port).RX_CTL);
+ Time.U_Delay (220);
+
+ Registers.Set_Mask
+ (Register => FDI_Regs (Port).RX_CTL,
+ Mask => FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_PCDCLK);
+ end Pre_Train;
+
+ procedure Train
+ (Port : in PCH.FDI_Port_Type;
+ TP : in Training_Pattern;
+ Success : out Boolean)
+ is
+ Lock_Bit : constant Word32 :=
+ (if TP = TP_1 then FDI_RX_BIT_LOCK else FDI_RX_SYMBOL_LOCK);
+
+ procedure Check_Lock (Lock_Bit : Word32)
+ is
+ begin
+ for I in 1 .. 5 loop
+ Registers.Is_Set_Mask
+ (Register => FDI_Regs (Port).RX_IIR,
+ Mask => Lock_Bit,
+ Result => Success);
+ if Success then
+ -- clear the lock bit
+ Registers.Write
+ (Register => FDI_Regs (Port).RX_IIR,
+ Value => Lock_Bit);
+ end if;
+ exit when Success;
+ Time.U_Delay (1);
+ end loop;
+ end Check_Lock;
+ begin
+ Registers.Unset_And_Set_Mask
+ (Register => FDI_Regs (Port).RX_CTL,
+ Mask_Unset => FDI_RX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_RX_CTL_FDI_RX_ENABLE or
+ FDI_RX_CTL_TRAINING_PATTERN (TP));
+ Registers.Posting_Read (FDI_Regs (Port).RX_CTL);
+
+ if TP <= TP_2 then
+ Time.U_Delay (1);
+ if TP = TP_1 then
+ Check_Lock (FDI_RX_BIT_LOCK);
+ else
+ Check_Lock (FDI_RX_SYMBOL_LOCK);
+ if Success then
+ Check_Lock (FDI_RX_INTERLANE_ALIGNMENT);
+ end if;
+ end if;
+ else
+ Time.U_Delay (31);
+ Success := True;
+ end if;
+ end Train;
+
+ procedure Auto_Train (Port : PCH.FDI_Port_Type)
+ is
+ begin
+ Registers.Set_Mask
+ (Register => FDI_Regs (Port).RX_CTL,
+ Mask => FDI_RX_CTL_FDI_RX_ENABLE or
+ FDI_RX_CTL_FDI_AUTO_TRAIN);
+ Registers.Posting_Read (FDI_Regs (Port).RX_CTL);
+
+ if Config.Has_FDI_RX_Power_Down then
+ Time.U_Delay (30);
+ Registers.Unset_And_Set_Mask
+ (Register => FDI_Regs (Port).RX_MISC,
+ Mask_Unset => FDI_RX_MISC_FDI_RX_PWRDN_LANE1_MASK or
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE0_MASK,
+ Mask_Set => FDI_RX_MISC_FDI_RX_PWRDN_LANE1 (0) or
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE0 (0));
+ Registers.Posting_Read (FDI_Regs (Port).RX_MISC);
+ end if;
+
+ Time.U_Delay (5);
+ end Auto_Train;
+
+ procedure Enable_EC (Port : PCH.FDI_Port_Type)
+ is
+ begin
+ Registers.Set_Mask
+ (Register => FDI_Regs (Port).RX_CTL,
+ Mask => FDI_RX_CTL_FS_ERROR_CORRECTION_ENABLE or
+ FDI_RX_CTL_FE_ERROR_CORRECTION_ENABLE);
+ end Enable_EC;
+
+ ----------------------------------------------------------------------------
+
+ procedure Off (Port : PCH.FDI_Port_Type; OT : Off_Type)
+ is
+ begin
+ Registers.Unset_Mask
+ (Register => FDI_Regs (Port).RX_CTL,
+ Mask => FDI_RX_CTL_FDI_RX_ENABLE or
+ FDI_RX_CTL_FDI_AUTO_TRAIN);
+
+ if Config.Has_FDI_RX_Power_Down and then OT >= Lanes_Off then
+ Registers.Unset_And_Set_Mask
+ (Register => FDI_Regs (Port).RX_MISC,
+ Mask_Unset => FDI_RX_MISC_FDI_RX_PWRDN_LANE1_MASK or
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE0_MASK,
+ Mask_Set => FDI_RX_MISC_FDI_RX_PWRDN_LANE1 (2) or
+ FDI_RX_MISC_FDI_RX_PWRDN_LANE0 (2));
+ Registers.Posting_Read (FDI_Regs (Port).RX_MISC);
+ end if;
+
+ if OT >= Clock_Off then
+ Registers.Unset_And_Set_Mask
+ (Register => FDI_Regs (Port).RX_CTL,
+ Mask_Unset => FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_MASK,
+ Mask_Set => FDI_RX_CTL_RAWCLK_TO_PCDCLK_SEL_RAWCLK);
+
+ Registers.Unset_Mask
+ (Register => FDI_Regs (Port).RX_CTL,
+ Mask => FDI_RX_CTL_FDI_PLL_ENABLE);
+ end if;
+ end Off;
+
+end HW.GFX.GMA.PCH.FDI;
diff --git a/common/hw-gfx-gma-pch-fdi.ads b/common/hw-gfx-gma-pch-fdi.ads
new file mode 100644
index 0000000..1de78d5
--- /dev/null
+++ b/common/hw-gfx-gma-pch-fdi.ads
@@ -0,0 +1,29 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.FDI is
+
+ type Training_Pattern is (TP_1, TP_2, TP_Idle, TP_None);
+
+ procedure Pre_Train (Port : PCH.FDI_Port_Type; Port_Cfg : Port_Config);
+ procedure Train
+ (Port : in PCH.FDI_Port_Type;
+ TP : in Training_Pattern;
+ Success : out Boolean);
+ procedure Auto_Train (Port : PCH.FDI_Port_Type);
+ procedure Enable_EC (Port : PCH.FDI_Port_Type);
+
+ type Off_Type is (Rx_Off, Lanes_Off, Clock_Off);
+ procedure Off (Port : PCH.FDI_Port_Type; OT : Off_Type);
+
+end HW.GFX.GMA.PCH.FDI;
diff --git a/common/hw-gfx-gma-pch-sideband.adb b/common/hw-gfx-gma-pch-sideband.adb
new file mode 100644
index 0000000..abbf472
--- /dev/null
+++ b/common/hw-gfx-gma-pch-sideband.adb
@@ -0,0 +1,137 @@
+--
+-- Copyright (C) 2015 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PCH.Sideband is
+
+ SBI_CTL_DEST_ICLK : constant := 0 * 2 ** 16;
+ SBI_CTL_DEST_MPHY : constant := 1 * 2 ** 16;
+ SBI_CTL_OP_IORD : constant := 2 * 2 ** 8;
+ SBI_CTL_OP_IOWR : constant := 3 * 2 ** 8;
+ SBI_CTL_OP_CRRD : constant := 6 * 2 ** 8;
+ SBI_CTL_OP_CRWR : constant := 7 * 2 ** 8;
+
+ SBI_RESPONSE_FAIL : constant := 1 * 2 ** 1;
+ SBI_BUSY : constant := 1 * 2 ** 0;
+
+ type Register_Array is array (Register_Type) of Word32;
+ Register_Addr : constant Register_Array := Register_Array'
+ (SBI_SSCDIVINTPHASE6 => 16#0600_0000#,
+ SBI_SSCCTL6 => 16#060c_0000#,
+ SBI_SSCAUXDIV => 16#0610_0000#);
+
+ ----------------------------------------------------------------------------
+
+ procedure Read
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Value : out Word32)
+ is
+ begin
+ Registers.Wait_Unset_Mask
+ (Register => Registers.SBI_CTL_STAT,
+ Mask => SBI_BUSY);
+
+ Registers.Write
+ (Register => Registers.SBI_ADDR,
+ Value => Register_Addr (Register));
+
+ if Dest = SBI_ICLK then
+ Registers.Write
+ (Register => Registers.SBI_CTL_STAT,
+ Value => SBI_CTL_DEST_ICLK or SBI_CTL_OP_CRRD or SBI_BUSY);
+ else
+ Registers.Write
+ (Register => Registers.SBI_CTL_STAT,
+ Value => SBI_CTL_DEST_MPHY or SBI_CTL_OP_IORD or SBI_BUSY);
+ end if;
+
+ Registers.Wait_Unset_Mask
+ (Register => Registers.SBI_CTL_STAT,
+ Mask => SBI_BUSY);
+
+ Registers.Read
+ (Register => Registers.SBI_DATA,
+ Value => Value);
+ end Read;
+
+ procedure Write
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Value : in Word32)
+ is
+ begin
+ Registers.Wait_Unset_Mask
+ (Register => Registers.SBI_CTL_STAT,
+ Mask => SBI_BUSY);
+
+ Registers.Write
+ (Register => Registers.SBI_ADDR,
+ Value => Register_Addr (Register));
+ Registers.Write
+ (Register => Registers.SBI_DATA,
+ Value => Value);
+
+ if Dest = SBI_ICLK then
+ Registers.Write
+ (Register => Registers.SBI_CTL_STAT,
+ Value => SBI_CTL_DEST_ICLK or SBI_CTL_OP_CRWR or SBI_BUSY);
+ else
+ Registers.Write
+ (Register => Registers.SBI_CTL_STAT,
+ Value => SBI_CTL_DEST_MPHY or SBI_CTL_OP_IOWR or SBI_BUSY);
+ end if;
+
+ Registers.Wait_Unset_Mask
+ (Register => Registers.SBI_CTL_STAT,
+ Mask => SBI_BUSY);
+ end Write;
+
+ ----------------------------------------------------------------------------
+
+ procedure Unset_Mask
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Mask : in Word32)
+ is
+ Value : Word32;
+ begin
+ Read (Dest, Register, Value);
+ Write (Dest, Register, Value and not Mask);
+ end Unset_Mask;
+
+ procedure Set_Mask
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Mask : in Word32)
+ is
+ Value : Word32;
+ begin
+ Read (Dest, Register, Value);
+ Write (Dest, Register, Value or Mask);
+ end Set_Mask;
+
+ procedure Unset_And_Set_Mask
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Mask_Unset : in Word32;
+ Mask_Set : in Word32)
+ is
+ Value : Word32;
+ begin
+ Read (Dest, Register, Value);
+ Write (Dest, Register, (Value and not Mask_Unset) or Mask_Set);
+ end Unset_And_Set_Mask;
+
+end HW.GFX.GMA.PCH.Sideband;
diff --git a/common/hw-gfx-gma-pch-sideband.ads b/common/hw-gfx-gma-pch-sideband.ads
new file mode 100644
index 0000000..2d29275
--- /dev/null
+++ b/common/hw-gfx-gma-pch-sideband.ads
@@ -0,0 +1,49 @@
+--
+-- Copyright (C) 2015 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PCH.Sideband is
+
+ type Destination_Type is (SBI_ICLK, SBI_MPHY);
+
+ type Register_Type is
+ (SBI_SSCDIVINTPHASE6,
+ SBI_SSCCTL6,
+ SBI_SSCAUXDIV);
+
+ procedure Read
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Value : out Word32);
+
+ procedure Write
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Value : in Word32);
+
+ procedure Unset_Mask
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Mask : in Word32);
+
+ procedure Set_Mask
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Mask : in Word32);
+
+ procedure Unset_And_Set_Mask
+ (Dest : in Destination_Type;
+ Register : in Register_Type;
+ Mask_Unset : in Word32;
+ Mask_Set : in Word32);
+
+end HW.GFX.GMA.PCH.Sideband;
diff --git a/common/hw-gfx-gma-pch-transcoder.adb b/common/hw-gfx-gma-pch-transcoder.adb
new file mode 100644
index 0000000..bab3f31
--- /dev/null
+++ b/common/hw-gfx-gma-pch-transcoder.adb
@@ -0,0 +1,280 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+-- Copyright (C) 2016 Nico Huber <nico.h@gmx.de>
+--
+-- 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Config;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PCH.Transcoder is
+
+ type DPLL_SEL_Array is array (FDI_Port_Type) of Word32;
+ DPLL_SEL_TRANSCODER_x_DPLL_ENABLE : constant DPLL_SEL_Array :=
+ (FDI_A => 1 * 2 ** 3,
+ FDI_B => 1 * 2 ** 7,
+ FDI_C => 1 * 2 ** 11);
+ DPLL_SEL_TRANSCODER_x_DPLL_SEL_MASK : constant DPLL_SEL_Array :=
+ (FDI_A => 1 * 2 ** 0,
+ FDI_B => 1 * 2 ** 4,
+ FDI_C => 1 * 2 ** 8);
+ function DPLL_SEL_TRANSCODER_x_DPLL_SEL
+ (Port : FDI_Port_Type;
+ PLL : Word32)
+ return Word32
+ is
+ begin
+ return Shift_Left (PLL,
+ (case Port is
+ when FDI_A => 0,
+ when FDI_B => 4,
+ when FDI_C => 8));
+ end DPLL_SEL_TRANSCODER_x_DPLL_SEL;
+
+ TRANS_CONF_TRANSCODER_ENABLE : constant := 1 * 2 ** 31;
+ TRANS_CONF_TRANSCODER_STATE : constant := 1 * 2 ** 30;
+
+ TRANS_CHICKEN2_TIMING_OVERRIDE : constant := 1 * 2 ** 31;
+
+ TRANS_DP_CTL_OUTPUT_ENABLE : constant := 1 * 2 ** 31;
+ TRANS_DP_CTL_PORT_SELECT_MASK : constant := 3 * 2 ** 29;
+ TRANS_DP_CTL_PORT_SELECT_NONE : constant := 3 * 2 ** 29;
+ TRANS_DP_CTL_ENHANCED_FRAMING : constant := 1 * 2 ** 18;
+ TRANS_DP_CTL_VSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 4;
+ TRANS_DP_CTL_HSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 3;
+
+ type TRANS_DP_CTL_PORT_SELECT_Array is array (PCH_Port) of Word32;
+ TRANS_DP_CTL_PORT_SELECT : constant TRANS_DP_CTL_PORT_SELECT_Array :=
+ (PCH_DP_B => 0 * 2 ** 29,
+ PCH_DP_C => 1 * 2 ** 29,
+ PCH_DP_D => 2 * 2 ** 29,
+ others => 0);
+
+ function TRANS_DP_CTL_BPC (BPC : BPC_Type) return Word32
+ is
+ begin
+ return
+ (case BPC is
+ when 6 => 2 * 2 ** 9,
+ when 10 => 1 * 2 ** 9,
+ when 12 => 3 * 2 ** 9,
+ when others => 0 * 2 ** 9);
+ end TRANS_DP_CTL_BPC;
+
+ function TRANS_DATA_M_TU (Transfer_Unit : Positive) return Word32 is
+ begin
+ return Shift_Left (Word32 (Transfer_Unit - 1), 25);
+ end TRANS_DATA_M_TU;
+
+ ----------------------------------------------------------------------------
+
+ type Transcoder_Registers is record
+ HTOTAL : Registers.Registers_Index;
+ HBLANK : Registers.Registers_Index;
+ HSYNC : Registers.Registers_Index;
+ VTOTAL : Registers.Registers_Index;
+ VBLANK : Registers.Registers_Index;
+ VSYNC : Registers.Registers_Index;
+ CONF : Registers.Registers_Index;
+ DP_CTL : Registers.Registers_Index;
+ DATA_M : Registers.Registers_Index;
+ DATA_N : Registers.Registers_Index;
+ LINK_M : Registers.Registers_Index;
+ LINK_N : Registers.Registers_Index;
+ CHICKEN2 : Registers.Registers_Index;
+ end record;
+
+ type Transcoder_Registers_Array is
+ array (FDI_Port_Type) of Transcoder_Registers;
+
+ TRANS : constant Transcoder_Registers_Array := Transcoder_Registers_Array'
+ (FDI_A =>
+ (HTOTAL => Registers.TRANS_HTOTAL_A,
+ HBLANK => Registers.TRANS_HBLANK_A,
+ HSYNC => Registers.TRANS_HSYNC_A,
+ VTOTAL => Registers.TRANS_VTOTAL_A,
+ VBLANK => Registers.TRANS_VBLANK_A,
+ VSYNC => Registers.TRANS_VSYNC_A,
+ CONF => Registers.TRANSACONF,
+ DP_CTL => Registers.TRANS_DP_CTL_A,
+ DATA_M => Registers.TRANSA_DATA_M1,
+ DATA_N => Registers.TRANSA_DATA_N1,
+ LINK_M => Registers.TRANSA_DP_LINK_M1,
+ LINK_N => Registers.TRANSA_DP_LINK_N1,
+ CHICKEN2 => Registers.TRANSA_CHICKEN2),
+ FDI_B =>
+ (HTOTAL => Registers.TRANS_HTOTAL_B,
+ HBLANK => Registers.TRANS_HBLANK_B,
+ HSYNC => Registers.TRANS_HSYNC_B,
+ VTOTAL => Registers.TRANS_VTOTAL_B,
+ VBLANK => Registers.TRANS_VBLANK_B,
+ VSYNC => Registers.TRANS_VSYNC_B,
+ CONF => Registers.TRANSBCONF,
+ DP_CTL => Registers.TRANS_DP_CTL_B,
+ DATA_M => Registers.TRANSB_DATA_M1,
+ DATA_N => Registers.TRANSB_DATA_N1,
+ LINK_M => Registers.TRANSB_DP_LINK_M1,
+ LINK_N => Registers.TRANSB_DP_LINK_N1,
+ CHICKEN2 => Registers.TRANSB_CHICKEN2),
+ FDI_C =>
+ (HTOTAL => Registers.TRANS_HTOTAL_C,
+ HBLANK => Registers.TRANS_HBLANK_C,
+ HSYNC => Registers.TRANS_HSYNC_C,
+ VTOTAL => Registers.TRANS_VTOTAL_C,
+ VBLANK => Registers.TRANS_VBLANK_C,
+ VSYNC => Registers.TRANS_VSYNC_C,
+ CONF => Registers.TRANSCCONF,
+ DP_CTL => Registers.TRANS_DP_CTL_C,
+ DATA_M => Registers.TRANSC_DATA_M1,
+ DATA_N => Registers.TRANSC_DATA_N1,
+ LINK_M => Registers.TRANSC_DP_LINK_M1,
+ LINK_N => Registers.TRANSC_DP_LINK_N1,
+ CHICKEN2 => Registers.TRANSC_CHICKEN2));
+
+ ----------------------------------------------------------------------------
+
+ procedure On
+ (Port_Cfg : Port_Config;
+ Port : FDI_Port_Type;
+ PLL : Word32)
+ is
+ Mode : constant Mode_Type := Port_Cfg.Mode;
+
+ function Encode (LSW, MSW : Pos16) return Word32 is
+ begin
+ return (Word32 (LSW) - 1) or ((Word32 (MSW) - 1) * 2 ** 16);
+ end Encode;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.Has_DPLL_SEL then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.PCH_DPLL_SEL,
+ Mask_Unset => DPLL_SEL_TRANSCODER_x_DPLL_SEL_MASK (Port),
+ Mask_Set => DPLL_SEL_TRANSCODER_x_DPLL_ENABLE (Port) or
+ DPLL_SEL_TRANSCODER_x_DPLL_SEL (Port, PLL));
+ end if;
+
+ Registers.Write
+ (Register => TRANS (Port).HTOTAL,
+ Value => Encode (Mode.H_Visible, Mode.H_Total));
+ Registers.Write
+ (Register => TRANS (Port).HBLANK,
+ Value => Encode (Mode.H_Visible, Mode.H_Total));
+ Registers.Write
+ (Register => TRANS (Port).HSYNC,
+ Value => Encode (Mode.H_Sync_Begin, Mode.H_Sync_End));
+ Registers.Write
+ (Register => TRANS (Port).VTOTAL,
+ Value => Encode (Mode.V_Visible, Mode.V_Total));
+ Registers.Write
+ (Register => TRANS (Port).VBLANK,
+ Value => Encode (Mode.V_Visible, Mode.V_Total));
+ Registers.Write
+ (Register => TRANS (Port).VSYNC,
+ Value => Encode (Mode.V_Sync_Begin, Mode.V_Sync_End));
+
+ if Port_Cfg.Display = DP then
+ declare
+ Data_M, Link_M : DP_Info.M_Type;
+ Data_N, Link_N : DP_Info.N_Type;
+ begin
+ DP_Info.Calculate_M_N
+ (Link => Port_Cfg.DP,
+ Mode => Port_Cfg.Mode,
+ Data_M => Data_M,
+ Data_N => Data_N,
+ Link_M => Link_M,
+ Link_N => Link_N);
+ Registers.Write
+ (Register => TRANS (Port).DATA_M,
+ Value => TRANS_DATA_M_TU (64) or
+ Word32 (Data_M));
+ Registers.Write
+ (Register => TRANS (Port).DATA_N,
+ Value => Word32 (Data_N));
+ Registers.Write
+ (Register => TRANS (Port).LINK_M,
+ Value => Word32 (Link_M));
+ Registers.Write
+ (Register => TRANS (Port).LINK_N,
+ Value => Word32 (Link_N));
+ end;
+
+ if Config.Has_Trans_DP_Ctl then
+ declare
+ Polarity : constant Word32 :=
+ (if Port_Cfg.Mode.H_Sync_Active_High then
+ TRANS_DP_CTL_HSYNC_ACTIVE_HIGH else 0) or
+ (if Port_Cfg.Mode.V_Sync_Active_High then
+ TRANS_DP_CTL_VSYNC_ACTIVE_HIGH else 0);
+ Enhanced_Framing : constant Word32 :=
+ (if Port_Cfg.DP.Enhanced_Framing then
+ TRANS_DP_CTL_ENHANCED_FRAMING else 0);
+ begin
+ Registers.Write
+ (Register => TRANS (Port).DP_CTL,
+ Value => TRANS_DP_CTL_OUTPUT_ENABLE or
+ TRANS_DP_CTL_PORT_SELECT (Port_Cfg.PCH_Port) or
+ Enhanced_Framing or
+ TRANS_DP_CTL_BPC (Port_Cfg.Mode.BPC) or
+ Polarity);
+ end;
+ end if;
+ end if;
+
+ if Config.Has_Trans_Timing_Ovrrde then
+ Registers.Set_Mask
+ (Register => TRANS (Port).CHICKEN2,
+ Mask => TRANS_CHICKEN2_TIMING_OVERRIDE);
+ end if;
+
+ Registers.Write
+ (Register => TRANS (Port).CONF,
+ Value => TRANS_CONF_TRANSCODER_ENABLE);
+ end On;
+
+ procedure Off (Port : FDI_Port_Type) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_Mask
+ (Register => TRANS (Port).CONF,
+ Mask => TRANS_CONF_TRANSCODER_ENABLE);
+ Registers.Wait_Unset_Mask
+ (Register => TRANS (Port).CONF,
+ Mask => TRANS_CONF_TRANSCODER_STATE,
+ TOut_MS => 50);
+
+ if Config.Has_Trans_Timing_Ovrrde then
+ Registers.Unset_Mask
+ (Register => TRANS (Port).CHICKEN2,
+ Mask => TRANS_CHICKEN2_TIMING_OVERRIDE);
+ end if;
+
+ if Config.Has_Trans_DP_Ctl then
+ Registers.Write
+ (Register => TRANS (Port).DP_CTL,
+ Value => TRANS_DP_CTL_PORT_SELECT_NONE);
+ end if;
+
+ if Config.Has_DPLL_SEL then
+ Registers.Unset_Mask
+ (Register => Registers.PCH_DPLL_SEL,
+ Mask => DPLL_SEL_TRANSCODER_x_DPLL_ENABLE (Port));
+ end if;
+ end Off;
+
+end HW.GFX.GMA.PCH.Transcoder;
diff --git a/common/hw-gfx-gma-pch-transcoder.ads b/common/hw-gfx-gma-pch-transcoder.ads
new file mode 100644
index 0000000..b1c5ca9
--- /dev/null
+++ b/common/hw-gfx-gma-pch-transcoder.ads
@@ -0,0 +1,24 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.Transcoder is
+
+ procedure On
+ (Port_Cfg : Port_Config;
+ Port : FDI_Port_Type;
+ PLL : Word32);
+
+ procedure Off
+ (Port : FDI_Port_Type);
+
+end HW.GFX.GMA.PCH.Transcoder;
diff --git a/common/hw-gfx-gma-pch-vga.adb b/common/hw-gfx-gma-pch-vga.adb
new file mode 100644
index 0000000..8e3f9c1
--- /dev/null
+++ b/common/hw-gfx-gma-pch-vga.adb
@@ -0,0 +1,171 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.PCH.Sideband;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.Word64;
+
+package body HW.GFX.GMA.PCH.VGA is
+
+ PCH_ADPA_DAC_ENABLE : constant := 1 * 2 ** 31;
+ PCH_ADPA_VSYNC_DISABLE : constant := 1 * 2 ** 11;
+ PCH_ADPA_HSYNC_DISABLE : constant := 1 * 2 ** 10;
+ PCH_ADPA_VSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 4;
+ PCH_ADPA_HSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 3;
+
+ PCH_ADPA_MASK : constant Word32 :=
+ PCH_TRANSCODER_SELECT_MASK or
+ PCH_ADPA_DAC_ENABLE or
+ PCH_ADPA_VSYNC_DISABLE or
+ PCH_ADPA_HSYNC_DISABLE or
+ PCH_ADPA_VSYNC_ACTIVE_HIGH or
+ PCH_ADPA_HSYNC_ACTIVE_HIGH;
+
+ ----------------------------------------------------------------------------
+
+ procedure On
+ (Port : FDI_Port_Type;
+ Mode : Mode_Type)
+ is
+ Polarity : Word32 := 0;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Mode.H_Sync_Active_High then
+ Polarity := Polarity or PCH_ADPA_HSYNC_ACTIVE_HIGH;
+ end if;
+ if Mode.V_Sync_Active_High then
+ Polarity := Polarity or PCH_ADPA_VSYNC_ACTIVE_HIGH;
+ end if;
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.PCH_ADPA,
+ Mask_Unset => PCH_ADPA_MASK,
+ Mask_Set => PCH_ADPA_DAC_ENABLE or
+ PCH_TRANSCODER_SELECT (Port) or
+ Polarity);
+ end On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Off
+ is
+ Sync_Disable : Word32 := 0;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.VGA_Has_Sync_Disable then
+ Sync_Disable := PCH_ADPA_HSYNC_DISABLE or PCH_ADPA_VSYNC_DISABLE;
+ end if;
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.PCH_ADPA,
+ Mask_Unset => PCH_ADPA_DAC_ENABLE,
+ Mask_Set => Sync_Disable);
+ end Off;
+
+ ----------------------------------------------------------------------------
+
+ PCH_PIXCLK_GATE_GATE : constant := 0 * 2 ** 0;
+ PCH_PIXCLK_GATE_UNGATE : constant := 1 * 2 ** 0;
+
+ SBI_SSCCTL_DISABLE : constant := 1 * 2 ** 0;
+ SBI_SSCDIVINTPHASE_DIVSEL_SHIFT : constant := 1;
+ SBI_SSCDIVINTPHASE_DIVSEL_MASK : constant := 16#7f# * 2 ** 1;
+ SBI_SSCDIVINTPHASE_INCVAL_SHIFT : constant := 8;
+ SBI_SSCDIVINTPHASE_INCVAL_MASK : constant := 16#7f# * 2 ** 8;
+ SBI_SSCDIVINTPHASE_DIR_SHIFT : constant := 15;
+ SBI_SSCDIVINTPHASE_DIR_MASK : constant := 16#01# * 2 ** 15;
+ SBI_SSCDIVINTPHASE_PROPAGATE : constant := 1 * 2 ** 0;
+ SBI_SSCAUXDIV_FINALDIV2SEL_SHIFT : constant := 4;
+ SBI_SSCAUXDIV_FINALDIV2SEL_MASK : constant := 16#01# * 2 ** 4;
+
+ function SBI_SSCDIVINTPHASE_DIVSEL (Val : Word32) return Word32 is
+ begin
+ return Shift_Left (Val, SBI_SSCDIVINTPHASE_DIVSEL_SHIFT);
+ end SBI_SSCDIVINTPHASE_DIVSEL;
+
+ function SBI_SSCDIVINTPHASE_INCVAL (Val : Word32) return Word32 is
+ begin
+ return Shift_Left (Val, SBI_SSCDIVINTPHASE_INCVAL_SHIFT);
+ end SBI_SSCDIVINTPHASE_INCVAL;
+
+ function SBI_SSCDIVINTPHASE_DIR (Val : Word32) return Word32 is
+ begin
+ return Shift_Left (Val, SBI_SSCDIVINTPHASE_DIR_SHIFT);
+ end SBI_SSCDIVINTPHASE_DIR;
+
+ function SBI_SSCAUXDIV_FINALDIV2SEL (Val : Word32) return Word32 is
+ begin
+ return Shift_Left (Val, SBI_SSCAUXDIV_FINALDIV2SEL_SHIFT);
+ end SBI_SSCAUXDIV_FINALDIV2SEL;
+
+ procedure Clock_On (Mode : Mode_Type)
+ is
+ Refclock : constant := 2_700_000_000;
+
+ Aux_Div,
+ Div_Sel,
+ Phase_Inc,
+ Phase_Dir : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Write (Registers.PCH_PIXCLK_GATE, PCH_PIXCLK_GATE_GATE);
+
+ Sideband.Set_Mask
+ (Dest => Sideband.SBI_ICLK,
+ Register => Sideband.SBI_SSCCTL6,
+ Mask => SBI_SSCCTL_DISABLE);
+
+ Aux_Div := 16#0000_0000#;
+ Div_Sel := Word32 (Refclock / Mode.Dotclock - 2);
+ Phase_Inc := Word32 ((Refclock * 64) / Mode.Dotclock) and 16#0000_003f#;
+ Phase_Dir := 16#0000_0000#;
+
+ pragma Debug (Debug.Put_Reg32 ("Aux_Div ", Aux_Div));
+ pragma Debug (Debug.Put_Reg32 ("Div_Sel ", Div_Sel));
+ pragma Debug (Debug.Put_Reg32 ("Phase_Inc", Phase_Inc));
+ pragma Debug (Debug.Put_Reg32 ("Phase_Dir", Phase_Dir));
+
+ Sideband.Unset_And_Set_Mask
+ (Dest => Sideband.SBI_ICLK,
+ Register => Sideband.SBI_SSCDIVINTPHASE6,
+ Mask_Unset => SBI_SSCDIVINTPHASE_DIVSEL_MASK or
+ SBI_SSCDIVINTPHASE_INCVAL_MASK or
+ SBI_SSCDIVINTPHASE_DIR_MASK,
+ Mask_Set => SBI_SSCDIVINTPHASE_DIVSEL (Div_Sel) or
+ SBI_SSCDIVINTPHASE_INCVAL (Phase_Inc) or
+ SBI_SSCDIVINTPHASE_DIR (Phase_Dir) or
+ SBI_SSCDIVINTPHASE_PROPAGATE);
+
+ Sideband.Unset_And_Set_Mask
+ (Dest => Sideband.SBI_ICLK,
+ Register => Sideband.SBI_SSCAUXDIV,
+ Mask_Unset => SBI_SSCAUXDIV_FINALDIV2SEL_MASK,
+ Mask_Set => SBI_SSCAUXDIV_FINALDIV2SEL (Aux_Div));
+
+ Sideband.Unset_Mask
+ (Dest => Sideband.SBI_ICLK,
+ Register => Sideband.SBI_SSCCTL6,
+ Mask => SBI_SSCCTL_DISABLE);
+
+ Registers.Write (Registers.PCH_PIXCLK_GATE, PCH_PIXCLK_GATE_UNGATE);
+ end Clock_On;
+
+end HW.GFX.GMA.PCH.VGA;
diff --git a/common/hw-gfx-gma-pch-vga.ads b/common/hw-gfx-gma-pch-vga.ads
new file mode 100644
index 0000000..95df5e5
--- /dev/null
+++ b/common/hw-gfx-gma-pch-vga.ads
@@ -0,0 +1,24 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.VGA is
+
+ procedure On
+ (Port : FDI_Port_Type;
+ Mode : Mode_Type);
+
+ procedure Off;
+
+ procedure Clock_On (Mode : Mode_Type);
+
+end HW.GFX.GMA.PCH.VGA;
diff --git a/common/hw-gfx-gma-pch.ads b/common/hw-gfx-gma-pch.ads
new file mode 100644
index 0000000..5826c1d
--- /dev/null
+++ b/common/hw-gfx-gma-pch.ads
@@ -0,0 +1,42 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Config;
+
+private package HW.GFX.GMA.PCH is
+
+ type FDI_Port_Type is (FDI_A, FDI_B, FDI_C);
+
+ ----------------------------------------------------------------------------
+
+ -- common to all PCH outputs
+
+ PCH_TRANSCODER_SELECT_SHIFT : constant :=
+ (case Config.CPU is
+ when Ironlake => 30,
+ when Sandybridge | Ivybridge => 29,
+ when Haswell | Broadwell | Skylake => 0);
+
+ PCH_TRANSCODER_SELECT_MASK : constant :=
+ (case Config.CPU is
+ when Ironlake => 1 * 2 ** 30,
+ when Sandybridge | Ivybridge => 3 * 2 ** 29,
+ when Haswell | Broadwell | Skylake => 0);
+
+ type PCH_TRANSCODER_SELECT_Array is array (FDI_Port_Type) of Word32;
+ PCH_TRANSCODER_SELECT : constant PCH_TRANSCODER_SELECT_Array :=
+ (FDI_A => 0 * 2 ** PCH_TRANSCODER_SELECT_SHIFT,
+ FDI_B => 1 * 2 ** PCH_TRANSCODER_SELECT_SHIFT,
+ FDI_C => 2 * 2 ** PCH_TRANSCODER_SELECT_SHIFT);
+
+end HW.GFX.GMA.PCH;
diff --git a/common/hw-gfx-gma-pipe_setup.adb b/common/hw-gfx-gma-pipe_setup.adb
new file mode 100644
index 0000000..1315c78
--- /dev/null
+++ b/common/hw-gfx-gma-pipe_setup.adb
@@ -0,0 +1,610 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+
+use type HW.Word64;
+use type HW.Pos16;
+use type HW.Int32;
+use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
+
+package body HW.GFX.GMA.Pipe_Setup is
+
+ DSPCNTR_ENABLE : constant := 1 * 2 ** 31;
+ DSPCNTR_GAMMA_CORRECTION : constant := 1 * 2 ** 30;
+ DSPCNTR_DISABLE_TRICKLE_FEED : constant := 1 * 2 ** 14;
+ DSPCNTR_FORMAT_MASK : constant := 15 * 2 ** 26;
+
+ DSPCNTR_MASK : constant Word32 :=
+ DSPCNTR_ENABLE or
+ DSPCNTR_GAMMA_CORRECTION or
+ DSPCNTR_FORMAT_MASK or
+ DSPCNTR_DISABLE_TRICKLE_FEED;
+
+ PLANE_CTL_PLANE_ENABLE : constant := 1 * 2 ** 31;
+ PLANE_CTL_SRC_PIX_FMT_RGB_32B_8888 : constant := 4 * 2 ** 24;
+ PLANE_CTL_PLANE_GAMMA_DISABLE : constant := 1 * 2 ** 13;
+
+ PLANE_WM_ENABLE : constant := 1 * 2 ** 31;
+ PLANE_WM_LINES_SHIFT : constant := 14;
+ PLANE_WM_LINES_MASK : constant := 16#001f# * 2 ** 14;
+ PLANE_WM_BLOCKS_MASK : constant := 16#03ff# * 2 ** 0;
+
+ SPCNTR_ENABLE : constant := 1 * 2 ** 31;
+
+ TRANS_CLK_SEL_PORT_NONE : constant := 0 * 2 ** 29;
+
+ type TRANS_CLK_SEL_PORT_Array is
+ array (Digital_Port) of Word32;
+ TRANS_CLK_SEL_PORT : constant TRANS_CLK_SEL_PORT_Array :=
+ TRANS_CLK_SEL_PORT_Array'
+ (DIGI_A => 0 * 2 ** 29, -- DDI A is not selectable
+ DIGI_B => 2 * 2 ** 29,
+ DIGI_C => 3 * 2 ** 29,
+ DIGI_D => 4 * 2 ** 29,
+ DIGI_E => 5 * 2 ** 29);
+
+ PIPECONF_ENABLE : constant := 1 * 2 ** 31;
+ PIPECONF_ENABLED_STATUS : constant := 1 * 2 ** 30;
+ PIPECONF_ENABLE_DITHER : constant := 1 * 2 ** 4;
+ PIPECONF_DITHER_TEMPORAL : constant := 1 * 2 ** 2;
+
+ PF_CTL_1_ENABLE : constant Word32 := 1 * 2 ** 31;
+
+ PS_CTRL_ENABLE_SCALER : constant Word32 := 1 * 2 ** 31;
+ PS_CTRL_SCALER_MODE_7X5_EXTENDED : constant Word32 := 1 * 2 ** 28;
+ PS_CTRL_FILTER_SELECT_MEDIUM_2 : constant Word32 := 1 * 2 ** 23;
+
+ PIPE_DDI_FUNC_CTL_ENABLE : constant := 1 * 2 ** 31;
+ PIPE_DDI_FUNC_CTL_DDI_SELECT_MASK : constant := 7 * 2 ** 28;
+ PIPE_DDI_FUNC_CTL_DDI_SELECT_NONE : constant := 0 * 2 ** 28;
+ PIPE_DDI_FUNC_CTL_DDI_SELECT_B : constant := 1 * 2 ** 28;
+ PIPE_DDI_FUNC_CTL_DDI_SELECT_C : constant := 2 * 2 ** 28;
+ PIPE_DDI_FUNC_CTL_DDI_SELECT_D : constant := 3 * 2 ** 28;
+ PIPE_DDI_FUNC_CTL_DDI_SELECT_E : constant := 4 * 2 ** 28;
+ PIPE_DDI_FUNC_CTL_MODE_SELECT_MASK : constant := 7 * 2 ** 24;
+ PIPE_DDI_FUNC_CTL_MODE_SELECT_HDMI : constant := 0 * 2 ** 24;
+ PIPE_DDI_FUNC_CTL_MODE_SELECT_DVI : constant := 1 * 2 ** 24;
+ PIPE_DDI_FUNC_CTL_MODE_SELECT_DP_SST : constant := 2 * 2 ** 24;
+ PIPE_DDI_FUNC_CTL_MODE_SELECT_DP_MST : constant := 3 * 2 ** 24;
+ PIPE_DDI_FUNC_CTL_MODE_SELECT_FDI : constant := 4 * 2 ** 24;
+ PIPE_DDI_FUNC_CTL_BPC_MASK : constant := 7 * 2 ** 20;
+ PIPE_DDI_FUNC_CTL_BPC_8BITS : constant := 0 * 2 ** 20;
+ PIPE_DDI_FUNC_CTL_BPC_10BITS : constant := 1 * 2 ** 20;
+ PIPE_DDI_FUNC_CTL_BPC_6BITS : constant := 2 * 2 ** 20;
+ PIPE_DDI_FUNC_CTL_BPC_12BITS : constant := 3 * 2 ** 20;
+ PIPE_DDI_FUNC_CTL_VSYNC_ACTIVE_LOW : constant := 0 * 2 ** 17;
+ PIPE_DDI_FUNC_CTL_VSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 17;
+ PIPE_DDI_FUNC_CTL_HSYNC_ACTIVE_LOW : constant := 0 * 2 ** 16;
+ PIPE_DDI_FUNC_CTL_HSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 16;
+ PIPE_DDI_FUNC_CTL_EDP_SELECT_MASK : constant := 7 * 2 ** 12;
+ PIPE_DDI_FUNC_CTL_EDP_SELECT_ALWAYS_ON : constant := 0 * 2 ** 12;
+ PIPE_DDI_FUNC_CTL_EDP_SELECT_A : constant := 4 * 2 ** 12;
+ PIPE_DDI_FUNC_CTL_EDP_SELECT_B : constant := 5 * 2 ** 12;
+ PIPE_DDI_FUNC_CTL_EDP_SELECT_C : constant := 6 * 2 ** 12;
+ PIPE_DDI_FUNC_CTL_DP_VC_PAYLOAD_ALLOC : constant := 1 * 2 ** 8;
+ PIPE_DDI_FUNC_CTL_BFI_ENABLE : constant := 1 * 2 ** 4;
+ PIPE_DDI_FUNC_CTL_PORT_WIDTH_MASK : constant := 7 * 2 ** 1;
+ PIPE_DDI_FUNC_CTL_PORT_WIDTH_1_LANE : constant := 0 * 2 ** 1;
+ PIPE_DDI_FUNC_CTL_PORT_WIDTH_2_LANES : constant := 1 * 2 ** 1;
+ PIPE_DDI_FUNC_CTL_PORT_WIDTH_4_LANES : constant := 3 * 2 ** 1;
+
+ type DDI_Select_Array is array (Digital_Port) of Word32;
+ PIPE_DDI_FUNC_CTL_DDI_SELECT : constant DDI_Select_Array :=
+ DDI_Select_Array'
+ (DIGI_A => PIPE_DDI_FUNC_CTL_DDI_SELECT_NONE,
+ DIGI_B => PIPE_DDI_FUNC_CTL_DDI_SELECT_B,
+ DIGI_C => PIPE_DDI_FUNC_CTL_DDI_SELECT_C,
+ DIGI_D => PIPE_DDI_FUNC_CTL_DDI_SELECT_D,
+ DIGI_E => PIPE_DDI_FUNC_CTL_DDI_SELECT_E);
+
+ type DDI_Mode_Array is array (Display_Type) of Word32;
+ PIPE_DDI_FUNC_CTL_MODE_SELECT : constant DDI_Mode_Array :=
+ DDI_Mode_Array'
+ (VGA => PIPE_DDI_FUNC_CTL_MODE_SELECT_FDI,
+ HDMI => PIPE_DDI_FUNC_CTL_MODE_SELECT_DVI,
+ DP => PIPE_DDI_FUNC_CTL_MODE_SELECT_DP_SST,
+ others => 0);
+
+ type HV_Sync_Array is array (Boolean) of Word32;
+ PIPE_DDI_FUNC_CTL_VSYNC : constant HV_Sync_Array := HV_Sync_Array'
+ (False => PIPE_DDI_FUNC_CTL_VSYNC_ACTIVE_LOW,
+ True => PIPE_DDI_FUNC_CTL_VSYNC_ACTIVE_HIGH);
+ PIPE_DDI_FUNC_CTL_HSYNC : constant HV_Sync_Array := HV_Sync_Array'
+ (False => PIPE_DDI_FUNC_CTL_HSYNC_ACTIVE_LOW,
+ True => PIPE_DDI_FUNC_CTL_HSYNC_ACTIVE_HIGH);
+
+ type EDP_Select_Array is array (Controller_Kind) of Word32;
+ PIPE_DDI_FUNC_CTL_EDP_SELECT : constant EDP_Select_Array :=
+ EDP_Select_Array'
+ (A => PIPE_DDI_FUNC_CTL_EDP_SELECT_ALWAYS_ON, -- we never use panel fitter
+ B => PIPE_DDI_FUNC_CTL_EDP_SELECT_B,
+ C => PIPE_DDI_FUNC_CTL_EDP_SELECT_C);
+ PIPE_DDI_FUNC_CTL_EDP_SELECT_ONOFF : constant EDP_Select_Array :=
+ EDP_Select_Array'
+ (A => PIPE_DDI_FUNC_CTL_EDP_SELECT_A,
+ B => PIPE_DDI_FUNC_CTL_EDP_SELECT_B,
+ C => PIPE_DDI_FUNC_CTL_EDP_SELECT_C);
+
+ type Port_Width_Array is array (HW.GFX.DP_Lane_Count) of Word32;
+ PIPE_DDI_FUNC_CTL_PORT_WIDTH : constant Port_Width_Array :=
+ Port_Width_Array'
+ (HW.GFX.DP_Lane_Count_1 => PIPE_DDI_FUNC_CTL_PORT_WIDTH_1_LANE,
+ HW.GFX.DP_Lane_Count_2 => PIPE_DDI_FUNC_CTL_PORT_WIDTH_2_LANES,
+ HW.GFX.DP_Lane_Count_4 => PIPE_DDI_FUNC_CTL_PORT_WIDTH_4_LANES);
+
+ function PIPE_DDI_FUNC_CTL_BPC (BPC : HW.GFX.BPC_Type) return Word32
+ is
+ Result : Word32;
+ begin
+ case BPC is
+ when 6 => Result := PIPE_DDI_FUNC_CTL_BPC_6BITS;
+ when 8 => Result := PIPE_DDI_FUNC_CTL_BPC_8BITS;
+ when 10 => Result := PIPE_DDI_FUNC_CTL_BPC_10BITS;
+ when 12 => Result := PIPE_DDI_FUNC_CTL_BPC_12BITS;
+ when others => Result := PIPE_DDI_FUNC_CTL_BPC_8BITS;
+ end case;
+ return Result;
+ end PIPE_DDI_FUNC_CTL_BPC;
+
+ function PIPE_DATA_M_TU (Transfer_Unit : Positive) return Word32 is
+ begin
+ return Shift_Left (Word32 (Transfer_Unit - 1), 25);
+ end PIPE_DATA_M_TU;
+
+ PIPE_MSA_MISC_SYNC_CLK : constant := 1 * 2 ** 0;
+ PIPE_MSA_MISC_BPC_6BITS : constant := 0 * 2 ** 5;
+ PIPE_MSA_MISC_BPC_8BITS : constant := 1 * 2 ** 5;
+ PIPE_MSA_MISC_BPC_10BITS : constant := 2 * 2 ** 5;
+ PIPE_MSA_MISC_BPC_12BITS : constant := 3 * 2 ** 5;
+ PIPE_MSA_MISC_BPC_16BITS : constant := 4 * 2 ** 5;
+
+ function PIPE_MSA_MISC_BPC (BPC : HW.GFX.BPC_Type) return Word32 is
+ Result : Word32;
+ begin
+ case BPC is
+ when 6 => Result := PIPE_MSA_MISC_BPC_6BITS;
+ when 8 => Result := PIPE_MSA_MISC_BPC_8BITS;
+ when 10 => Result := PIPE_MSA_MISC_BPC_10BITS;
+ when 12 => Result := PIPE_MSA_MISC_BPC_12BITS;
+ --when 16 => Result := PIPE_MSA_MISC_BPC_16BITS;
+ when others => Result := PIPE_MSA_MISC_BPC_8BITS;
+ end case;
+ return Result;
+ end PIPE_MSA_MISC_BPC;
+
+ ---------------------------------------------------------------------------
+
+ function PIPECONF_BPC_MAP (Bits_Per_Color : HW.GFX.BPC_Type) return Word32
+ is
+ Result : Word32;
+ begin
+ if Bits_Per_Color = 6 then
+ Result := 2 * 2 ** 5;
+ elsif Bits_Per_Color = 10 then
+ Result := 1 * 2 ** 5;
+ elsif Bits_Per_Color = 12 then
+ Result := 3 * 2 ** 5;
+ else
+ Result := 0;
+ end if;
+ return Result;
+ end PIPECONF_BPC_MAP;
+
+ ---------------------------------------------------------------------------
+
+ function PLANE_WM_LINES (Lines : Natural) return Word32 is
+ begin
+ return Shift_Left (Word32 (Lines), PLANE_WM_LINES_SHIFT)
+ and PLANE_WM_LINES_MASK;
+ end PLANE_WM_LINES;
+
+ function PLANE_WM_BLOCKS (Blocks : Natural) return Word32 is
+ begin
+ return Word32 (Blocks) and PLANE_WM_BLOCKS_MASK;
+ end PLANE_WM_BLOCKS;
+
+ ---------------------------------------------------------------------------
+
+ function Encode (LSW, MSW : Pos16) return Word32 is
+ begin
+ return Shift_Left (Word32 (MSW - 1), 16) or Word32 (LSW - 1);
+ end Encode;
+
+ ----------------------------------------------------------------------------
+
+ procedure Setup_Link
+ (Head : Head_Type;
+ Link : DP_Link;
+ Mode : Mode_Type)
+ with
+ Global => (In_Out => Registers.Register_State),
+ Depends => (Registers.Register_State =>+ (Head, Link, Mode))
+ is
+ Data_M, Link_M : DP_Info.M_Type;
+ Data_N, Link_N : DP_Info.N_Type;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ DP_Info.Calculate_M_N
+ (Link => Link,
+ Mode => Mode,
+ Data_M => Data_M,
+ Data_N => Data_N,
+ Link_M => Link_M,
+ Link_N => Link_N);
+
+ Registers.Write
+ (Register => Head.PIPE_DATA_M1,
+ Value => PIPE_DATA_M_TU (64) or
+ Word32 (Data_M));
+ Registers.Write
+ (Register => Head.PIPE_DATA_N1,
+ Value => Word32 (Data_N));
+
+ Registers.Write
+ (Register => Head.PIPE_LINK_M1,
+ Value => Word32 (Link_M));
+ Registers.Write
+ (Register => Head.PIPE_LINK_N1,
+ Value => Word32 (Link_N));
+
+ if Config.Has_Pipe_MSA_Misc then
+ Registers.Write
+ (Register => Head.PIPE_MSA_MISC,
+ Value => PIPE_MSA_MISC_SYNC_CLK or
+ PIPE_MSA_MISC_BPC (Mode.BPC));
+ end if;
+ end Setup_Link;
+
+ ----------------------------------------------------------------------------
+
+ procedure Clear_Watermarks (Controller : Controller_Type) is
+ begin
+ Registers.Write
+ (Register => Controller.PLANE_BUF_CFG,
+ Value => 16#0000_0000#);
+ for Level in WM_Levels range 0 .. WM_Levels'Last loop
+ Registers.Write
+ (Register => Controller.PLANE_WM (Level),
+ Value => 16#0000_0000#);
+ end loop;
+ Registers.Write
+ (Register => Controller.WM_LINETIME,
+ Value => 16#0000_0000#);
+ end Clear_Watermarks;
+
+ procedure Setup_Watermarks (Controller : Controller_Type)
+ is
+ type Per_Plane_Buffer_Range is array (Controller_Kind) of Word32;
+ Buffer_Range : constant Per_Plane_Buffer_Range := Per_Plane_Buffer_Range'
+ (A => Shift_Left (159, 16) or 0,
+ B => Shift_Left (319, 16) or 160,
+ C => Shift_Left (479, 16) or 320);
+ begin
+ Registers.Write
+ (Register => Controller.PLANE_BUF_CFG,
+ Value => Buffer_Range (Controller.Kind));
+ Registers.Write
+ (Register => Controller.PLANE_WM (0),
+ Value => PLANE_WM_ENABLE or
+ PLANE_WM_LINES (2) or
+ PLANE_WM_BLOCKS (160));
+ end Setup_Watermarks;
+
+ ----------------------------------------------------------------------------
+
+ procedure Setup_Display
+ (Controller : in Controller_Type;
+ Head : in Head_Type;
+ Mode : in HW.GFX.Mode_Type;
+ Framebuffer : in HW.GFX.Framebuffer_Type)
+ with
+ Global => (In_Out => Registers.Register_State),
+ Depends =>
+ (Registers.Register_State
+ =>+
+ (Registers.Register_State,
+ Controller,
+ Head,
+ Mode,
+ Framebuffer))
+ is
+ -- FIXME: setup correct format, based on framebuffer RGB format
+ Format : constant Word32 := 6 * 2 ** 26;
+ PRI : Word32 := DSPCNTR_ENABLE or Format;
+
+ function To_Bytes (Pixels : Width_Type) return Word32
+ with
+ Pre => (Word32 (Pixels) <= Word32'Last / 4 / Word32 (BPC_Type'Last) * 8)
+ is
+ begin
+ return Word32 (Pos64 (Pixels) * 4 * Framebuffer.BPC / 8);
+ end To_Bytes;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Write (Controller.PIPESRC, Encode (Mode.V_Visible, Mode.H_Visible));
+
+ if Config.Has_Plane_Control then
+ Setup_Watermarks (Controller);
+ Registers.Write
+ (Register => Controller.PLANE_CTL,
+ Value => PLANE_CTL_PLANE_ENABLE or
+ PLANE_CTL_SRC_PIX_FMT_RGB_32B_8888 or
+ PLANE_CTL_PLANE_GAMMA_DISABLE);
+ Registers.Write (Controller.PLANE_OFFSET, 16#0000_0000#);
+ Registers.Write (Controller.PLANE_SIZE, Encode (Mode.H_Visible, Mode.V_Visible));
+ Registers.Write (Controller.PLANE_STRIDE, To_Bytes (Framebuffer.Stride) / 64);
+ Registers.Write (Controller.PLANE_POS, 16#0000_0000#);
+ Registers.Write (Controller.PLANE_SURF, Framebuffer.Offset and 16#ffff_f000#);
+ else
+ if Config.Disable_Trickle_Feed then
+ PRI := PRI or DSPCNTR_DISABLE_TRICKLE_FEED;
+ end if;
+ -- for now, just disable gamma LUT (can't do anything
+ -- useful without colorimetry information from display)
+ Registers.Unset_And_Set_Mask
+ (Register => Controller.DSPCNTR,
+ Mask_Unset => DSPCNTR_MASK,
+ Mask_Set => PRI);
+
+ Registers.Write (Controller.DSPSTRIDE, To_Bytes (Framebuffer.Stride));
+ Registers.Write (Controller.DSPSURF, Framebuffer.Offset and 16#ffff_f000#);
+ if Config.Has_DSP_Linoff then
+ Registers.Write (Controller.DSPLINOFF, 0);
+ end if;
+ Registers.Write (Controller.DSPTILEOFF, 0);
+ end if;
+
+ Registers.Write (Head.HTOTAL, Encode (Mode.H_Visible, Mode.H_Total));
+ Registers.Write (Head.HBLANK, Encode (Mode.H_Visible, Mode.H_Total));
+ Registers.Write (Head.HSYNC, Encode (Mode.H_Sync_Begin, Mode.H_Sync_End));
+ Registers.Write (Head.VTOTAL, Encode (Mode.V_Visible, Mode.V_Total));
+ Registers.Write (Head.VBLANK, Encode (Mode.V_Visible, Mode.V_Total));
+ Registers.Write (Head.VSYNC, Encode (Mode.V_Sync_Begin, Mode.V_Sync_End));
+ end Setup_Display;
+
+ ----------------------------------------------------------------------------
+
+ procedure Setup_Head
+ (Controller : Controller_Type;
+ Head : Head_Type;
+ Port_Cfg : Port_Config;
+ Framebuffer : Framebuffer_Type)
+ is
+ PIPECONF_Options : Word32 := 0;
+ begin
+ if Config.Has_Pipe_DDI_Func then
+ Registers.Write
+ (Register => Head.PIPE_DDI_FUNC_CTL,
+ Value => PIPE_DDI_FUNC_CTL_ENABLE or
+ PIPE_DDI_FUNC_CTL_DDI_SELECT (Port_Cfg.Port) or
+ PIPE_DDI_FUNC_CTL_MODE_SELECT (Port_Cfg.Display) or
+ PIPE_DDI_FUNC_CTL_BPC (Port_Cfg.Mode.BPC) or
+ PIPE_DDI_FUNC_CTL_VSYNC (Port_Cfg.Mode.V_Sync_Active_High) or
+ PIPE_DDI_FUNC_CTL_HSYNC (Port_Cfg.Mode.H_Sync_Active_High) or
+ PIPE_DDI_FUNC_CTL_EDP_SELECT (Controller.Kind) or
+ PIPE_DDI_FUNC_CTL_PORT_WIDTH (Port_Cfg.DP.Lane_Count));
+ end if;
+
+ if Config.Has_Pipeconf_BPC then
+ PIPECONF_Options := PIPECONF_BPC_MAP (Port_Cfg.Mode.BPC);
+ end if;
+
+ -- Enable dithering if framebuffer BPC differs from connector BPC,
+ -- as smooth gradients look really bad without
+ if Framebuffer.BPC /= Port_Cfg.Mode.BPC then
+ PIPECONF_Options := PIPECONF_Options or PIPECONF_ENABLE_DITHER;
+ end if;
+
+ if not Config.Has_Pipeconf_Misc then
+ Registers.Write
+ (Register => Head.PIPECONF,
+ Value => PIPECONF_ENABLE or PIPECONF_Options);
+ else
+ Registers.Write
+ (Register => Controller.PIPEMISC,
+ Value => PIPECONF_Options);
+ Registers.Write
+ (Register => Head.PIPECONF,
+ Value => PIPECONF_ENABLE);
+ end if;
+ Registers.Posting_Read (Head.PIPECONF);
+ end Setup_Head;
+
+ ----------------------------------------------------------------------------
+
+ procedure On
+ (Controller : Controller_Type;
+ Head : Head_Type;
+ Port_Cfg : Port_Config;
+ Framebuffer : Framebuffer_Type)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.Has_Trans_Clk_Sel then
+ Registers.Write
+ (Register => Controller.TRANS_CLK_SEL,
+ Value => TRANS_CLK_SEL_PORT (Port_Cfg.Port));
+ end if;
+
+ if Port_Cfg.Is_FDI then
+ Setup_Link (Head, Port_Cfg.FDI, Port_Cfg.Mode);
+ elsif Port_Cfg.Display = DP then
+ Setup_Link (Head, Port_Cfg.DP, Port_Cfg.Mode);
+ end if;
+
+ Setup_Display (Controller, Head, Port_Cfg.Mode, Framebuffer);
+
+ Setup_Head (Controller, Head, Port_Cfg, Framebuffer);
+ end On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Planes_Off (Controller : Controller_Type) is
+ begin
+ Registers.Unset_Mask (Controller.SPCNTR, SPCNTR_ENABLE);
+ if Config.Has_Plane_Control then
+ Clear_Watermarks (Controller);
+ Registers.Unset_Mask (Controller.PLANE_CTL, PLANE_CTL_PLANE_ENABLE);
+ Registers.Write (Controller.PLANE_SURF, 16#0000_0000#);
+ else
+ Registers.Unset_Mask (Controller.DSPCNTR, DSPCNTR_ENABLE);
+ end if;
+ end Planes_Off;
+
+ procedure Head_Off (Head : Head_Type)
+ is
+ Enabled : Boolean;
+ begin
+ Registers.Is_Set_Mask (Head.PIPECONF, PIPECONF_ENABLE, Enabled);
+
+ if Enabled then
+ Registers.Unset_Mask (Head.PIPECONF, PIPECONF_ENABLE);
+ end if;
+
+ -- Workaround for Broadwell:
+ -- Status may be wrong if pipe hasn't been enabled since reset.
+ if not Config.Pipe_Enabled_Workaround or else Enabled then
+ -- synchronously wait until pipe is truly off
+ Registers.Wait_Unset_Mask
+ (Register => Head.PIPECONF,
+ Mask => PIPECONF_ENABLED_STATUS,
+ TOut_MS => 40);
+ end if;
+
+ if Config.Has_Pipe_DDI_Func then
+ Registers.Write (Head.PIPE_DDI_FUNC_CTL, 0);
+ end if;
+ end Head_Off;
+
+ procedure Panel_Fitter_Off (Controller : Controller_Type) is
+ begin
+ -- Writes to WIN_SZ arm the PS/PF registers.
+ if Config.Has_Plane_Control then
+ Registers.Unset_Mask (Controller.PS_CTRL_1, PS_CTRL_ENABLE_SCALER);
+ Registers.Write (Controller.PS_WIN_SZ_1, 16#0000_0000#);
+ if Controller.PS_CTRL_2 /= Registers.Invalid_Register and
+ Controller.PS_WIN_SZ_2 /= Registers.Invalid_Register
+ then
+ Registers.Unset_Mask (Controller.PS_CTRL_2, PS_CTRL_ENABLE_SCALER);
+ Registers.Write (Controller.PS_WIN_SZ_2, 16#0000_0000#);
+ end if;
+ else
+ Registers.Unset_Mask (Controller.PF_CTL_1, PF_CTL_1_ENABLE);
+ Registers.Write (Controller.PF_WIN_SZ, 16#0000_0000#);
+ end if;
+ end Panel_Fitter_Off;
+
+ procedure Trans_Clk_Off (Controller : Controller_Type) is
+ begin
+ if Config.Has_Trans_Clk_Sel then
+ Registers.Write (Controller.TRANS_CLK_SEL, TRANS_CLK_SEL_PORT_NONE);
+ end if;
+ end Trans_Clk_Off;
+
+ procedure Off (Controller : Controller_Type; Head : Head_Type) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Planes_Off (Controller);
+ Head_Off (Head);
+ Panel_Fitter_Off (Controller);
+ Trans_Clk_Off (Controller);
+ end Off;
+
+ procedure All_Off
+ is
+ EDP_Enabled, EDP_Piped : Boolean;
+
+ procedure EDP_Piped_To (Kind : Controller_Kind; Piped_To : out Boolean)
+ is
+ Pipe_DDI_Func_Ctl : Word32;
+ begin
+ Registers.Read (Registers.PIPE_EDP_DDI_FUNC_CTL, Pipe_DDI_Func_Ctl);
+ Pipe_DDI_Func_Ctl :=
+ Pipe_DDI_Func_Ctl and PIPE_DDI_FUNC_CTL_EDP_SELECT_MASK;
+
+ Piped_To := (Kind = A and Pipe_DDI_Func_Ctl = PIPE_DDI_FUNC_CTL_EDP_SELECT_ALWAYS_ON) or
+ Pipe_DDI_Func_Ctl = PIPE_DDI_FUNC_CTL_EDP_SELECT_ONOFF (Kind);
+ end EDP_Piped_To;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.Has_EDP_Pipe then
+ Registers.Is_Set_Mask
+ (Registers.PIPE_EDP_CONF, PIPECONF_ENABLE, EDP_Enabled);
+ else
+ EDP_Enabled := False;
+ end if;
+
+ for Kind in Controller_Kind loop
+ Planes_Off (Controllers (Kind));
+ if EDP_Enabled then
+ EDP_Piped_To (Kind, EDP_Piped);
+ if EDP_Piped then
+ Head_Off (Heads (Head_EDP));
+ EDP_Enabled := False;
+ end if;
+ end if;
+ Head_Off (Default_Pipe_Head (Kind));
+ Panel_Fitter_Off (Controllers (Kind));
+ Trans_Clk_Off (Controllers (Kind));
+ end loop;
+
+ if EDP_Enabled then
+ Head_Off (Heads (Head_EDP));
+ end if;
+ end All_Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure Update_Offset
+ (Controller : Controller_Type;
+ Framebuffer : HW.GFX.Framebuffer_Type) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Write (Controller.DSPSURF, Framebuffer.Offset and 16#ffff_f000#);
+ end Update_Offset;
+
+ ----------------------------------------------------------------------------
+
+ function Get_Pipe_Hint (Head : Head_Type) return Word32
+ is
+ type Pipe_Hint_Array is array (Pipe_Head) of Word32;
+ Pipe_Hint : constant Pipe_Hint_Array := Pipe_Hint_Array'
+ (Head_EDP => 0, Head_A => 0, Head_B => 1, Head_C => 2);
+ begin
+ return Pipe_Hint (Head.Head);
+ end Get_Pipe_Hint;
+
+ ----------------------------------------------------------------------------
+
+ function Default_Pipe_Head (Kind : Controller_Kind) return Head_Type
+ is
+ type Default_Head_Array is array (Controller_Kind) of Head_Type;
+ Default_Head : constant Default_Head_Array := Default_Head_Array'
+ (A => Heads (Head_A), B => Heads (Head_B), C => Heads (Head_C));
+ begin
+ return Default_Head (Kind);
+ end Default_Pipe_Head;
+
+end HW.GFX.GMA.Pipe_Setup;
diff --git a/common/hw-gfx-gma-pipe_setup.ads b/common/hw-gfx-gma-pipe_setup.ads
new file mode 100644
index 0000000..416cf01
--- /dev/null
+++ b/common/hw-gfx-gma-pipe_setup.ads
@@ -0,0 +1,274 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Registers;
+
+private package HW.GFX.GMA.Pipe_Setup
+is
+
+ type Controller_Kind is (A, B, C);
+ type Controller_Type is private;
+ type Controller_Array is array (Controller_Kind) of Controller_Type;
+
+ type Pipe_Head is (Head_EDP, Head_A, Head_B, Head_C);
+ type Head_Type is private;
+ type Head_Array is array (Pipe_Head) of Head_Type;
+
+ procedure On
+ (Controller : Controller_Type;
+ Head : Head_Type;
+ Port_Cfg : Port_Config;
+ Framebuffer : Framebuffer_Type);
+
+ procedure Off
+ (Controller : Controller_Type;
+ Head : Head_Type);
+
+ procedure All_Off;
+
+ function Get_Pipe_Hint (Head : Head_Type) return Word32;
+
+ procedure Update_Offset
+ (Controller : Controller_Type;
+ Framebuffer : HW.GFX.Framebuffer_Type);
+
+ Controllers : constant Controller_Array;
+
+ Heads : constant Head_Array;
+
+ function Default_Pipe_Head (Kind : Controller_Kind) return Head_Type;
+
+private
+
+ subtype WM_Levels is Natural range 0 .. 7;
+ type PLANE_WM_Type is array (WM_Levels) of Registers.Registers_Index;
+
+ type Controller_Type is
+ record
+ Kind : Controller_Kind;
+ PIPESRC : Registers.Registers_Index;
+ PIPEMISC : Registers.Registers_Index;
+ PF_CTL_1 : Registers.Registers_Index;
+ PF_WIN_POS : Registers.Registers_Index;
+ PF_WIN_SZ : Registers.Registers_Index;
+ DSPCNTR : Registers.Registers_Index;
+ DSPLINOFF : Registers.Registers_Index;
+ DSPSTRIDE : Registers.Registers_Index;
+ DSPSURF : Registers.Registers_Index;
+ DSPTILEOFF : Registers.Registers_Index;
+ SPCNTR : Registers.Registers_Index;
+ TRANS_CLK_SEL : Registers.Registers_Index;
+ -- Skylake registers (partially aliased)
+ PLANE_CTL : Registers.Registers_Index;
+ PLANE_OFFSET : Registers.Registers_Index;
+ PLANE_POS : Registers.Registers_Index;
+ PLANE_SIZE : Registers.Registers_Index;
+ PLANE_STRIDE : Registers.Registers_Index;
+ PLANE_SURF : Registers.Registers_Index;
+ PS_CTRL_1 : Registers.Registers_Index;
+ PS_CTRL_2 : Registers.Registers_Invalid_Index;
+ PS_WIN_SZ_1 : Registers.Registers_Index;
+ PS_WIN_SZ_2 : Registers.Registers_Invalid_Index;
+ WM_LINETIME : Registers.Registers_Index;
+ PLANE_BUF_CFG : Registers.Registers_Index;
+ PLANE_WM : PLANE_WM_Type;
+ end record;
+
+ type Head_Type is
+ record
+ Head : Pipe_Head;
+ HTOTAL : Registers.Registers_Index;
+ HBLANK : Registers.Registers_Index;
+ HSYNC : Registers.Registers_Index;
+ VTOTAL : Registers.Registers_Index;
+ VBLANK : Registers.Registers_Index;
+ VSYNC : Registers.Registers_Index;
+ PIPECONF : Registers.Registers_Index;
+ PIPE_DATA_M1 : Registers.Registers_Index;
+ PIPE_DATA_N1 : Registers.Registers_Index;
+ PIPE_LINK_M1 : Registers.Registers_Index;
+ PIPE_LINK_N1 : Registers.Registers_Index;
+ PIPE_DDI_FUNC_CTL : Registers.Registers_Index;
+ PIPE_MSA_MISC : Registers.Registers_Index;
+ end record;
+
+ Controllers : constant Controller_Array := Controller_Array'
+ (A => Controller_Type'
+ (Kind => A,
+ PIPESRC => Registers.PIPEASRC,
+ PIPEMISC => Registers.PIPEAMISC,
+ PF_CTL_1 => Registers.PFA_CTL_1,
+ PF_WIN_POS => Registers.PFA_WIN_POS,
+ PF_WIN_SZ => Registers.PFA_WIN_SZ,
+ DSPCNTR => Registers.DSPACNTR,
+ DSPLINOFF => Registers.DSPALINOFF,
+ DSPSTRIDE => Registers.DSPASTRIDE,
+ DSPSURF => Registers.DSPASURF,
+ DSPTILEOFF => Registers.DSPATILEOFF,
+ SPCNTR => Registers.SPACNTR,
+ TRANS_CLK_SEL => Registers.TRANSA_CLK_SEL,
+ PLANE_CTL => Registers.DSPACNTR,
+ PLANE_OFFSET => Registers.DSPATILEOFF,
+ PLANE_POS => Registers.PLANE_POS_1_A,
+ PLANE_SIZE => Registers.PLANE_SIZE_1_A,
+ PLANE_STRIDE => Registers.DSPASTRIDE,
+ PLANE_SURF => Registers.DSPASURF,
+ PS_CTRL_1 => Registers.PS_CTRL_1_A,
+ PS_CTRL_2 => Registers.PS_CTRL_2_A,
+ PS_WIN_SZ_1 => Registers.PS_WIN_SZ_1_A,
+ PS_WIN_SZ_2 => Registers.PS_WIN_SZ_2_A,
+ WM_LINETIME => Registers.WM_LINETIME_A,
+ PLANE_BUF_CFG => Registers.PLANE_BUF_CFG_1_A,
+ PLANE_WM => PLANE_WM_Type'(
+ Registers.PLANE_WM_1_A_0,
+ Registers.PLANE_WM_1_A_1,
+ Registers.PLANE_WM_1_A_2,
+ Registers.PLANE_WM_1_A_3,
+ Registers.PLANE_WM_1_A_4,
+ Registers.PLANE_WM_1_A_5,
+ Registers.PLANE_WM_1_A_6,
+ Registers.PLANE_WM_1_A_7)),
+ B => Controller_Type'
+ (Kind => B,
+ PIPESRC => Registers.PIPEBSRC,
+ PIPEMISC => Registers.PIPEBMISC,
+ PF_CTL_1 => Registers.PFB_CTL_1,
+ PF_WIN_POS => Registers.PFB_WIN_POS,
+ PF_WIN_SZ => Registers.PFB_WIN_SZ,
+ DSPCNTR => Registers.DSPBCNTR,
+ DSPLINOFF => Registers.DSPBLINOFF,
+ DSPSTRIDE => Registers.DSPBSTRIDE,
+ DSPSURF => Registers.DSPBSURF,
+ DSPTILEOFF => Registers.DSPBTILEOFF,
+ SPCNTR => Registers.SPBCNTR,
+ TRANS_CLK_SEL => Registers.TRANSB_CLK_SEL,
+ PLANE_CTL => Registers.DSPBCNTR,
+ PLANE_OFFSET => Registers.DSPBTILEOFF,
+ PLANE_POS => Registers.PLANE_POS_1_B,
+ PLANE_SIZE => Registers.PLANE_SIZE_1_B,
+ PLANE_STRIDE => Registers.DSPBSTRIDE,
+ PLANE_SURF => Registers.DSPBSURF,
+ PS_CTRL_1 => Registers.PS_CTRL_1_B,
+ PS_CTRL_2 => Registers.PS_CTRL_2_B,
+ PS_WIN_SZ_1 => Registers.PS_WIN_SZ_1_B,
+ PS_WIN_SZ_2 => Registers.PS_WIN_SZ_2_B,
+ WM_LINETIME => Registers.WM_LINETIME_B,
+ PLANE_BUF_CFG => Registers.PLANE_BUF_CFG_1_B,
+ PLANE_WM => PLANE_WM_Type'(
+ Registers.PLANE_WM_1_B_0,
+ Registers.PLANE_WM_1_B_1,
+ Registers.PLANE_WM_1_B_2,
+ Registers.PLANE_WM_1_B_3,
+ Registers.PLANE_WM_1_B_4,
+ Registers.PLANE_WM_1_B_5,
+ Registers.PLANE_WM_1_B_6,
+ Registers.PLANE_WM_1_B_7)),
+ C => Controller_Type'
+ (Kind => C,
+ PIPESRC => Registers.PIPECSRC,
+ PIPEMISC => Registers.PIPECMISC,
+ PF_CTL_1 => Registers.PFC_CTL_1,
+ PF_WIN_POS => Registers.PFC_WIN_POS,
+ PF_WIN_SZ => Registers.PFC_WIN_SZ,
+ DSPCNTR => Registers.DSPCCNTR,
+ DSPLINOFF => Registers.DSPCLINOFF,
+ DSPSTRIDE => Registers.DSPCSTRIDE,
+ DSPSURF => Registers.DSPCSURF,
+ DSPTILEOFF => Registers.DSPCTILEOFF,
+ SPCNTR => Registers.SPCCNTR,
+ TRANS_CLK_SEL => Registers.TRANSC_CLK_SEL,
+ PLANE_CTL => Registers.DSPCCNTR,
+ PLANE_OFFSET => Registers.DSPCTILEOFF,
+ PLANE_POS => Registers.PLANE_POS_1_C,
+ PLANE_SIZE => Registers.PLANE_SIZE_1_C,
+ PLANE_STRIDE => Registers.DSPCSTRIDE,
+ PLANE_SURF => Registers.DSPCSURF,
+ PS_CTRL_1 => Registers.PS_CTRL_1_C,
+ PS_CTRL_2 => Registers.Invalid_Register,
+ PS_WIN_SZ_1 => Registers.PS_WIN_SZ_1_C,
+ PS_WIN_SZ_2 => Registers.Invalid_Register,
+ WM_LINETIME => Registers.WM_LINETIME_C,
+ PLANE_BUF_CFG => Registers.PLANE_BUF_CFG_1_C,
+ PLANE_WM => PLANE_WM_Type'(
+ Registers.PLANE_WM_1_C_0,
+ Registers.PLANE_WM_1_C_1,
+ Registers.PLANE_WM_1_C_2,
+ Registers.PLANE_WM_1_C_3,
+ Registers.PLANE_WM_1_C_4,
+ Registers.PLANE_WM_1_C_5,
+ Registers.PLANE_WM_1_C_6,
+ Registers.PLANE_WM_1_C_7)));
+
+ Heads : constant Head_Array := Head_Array'
+ (Head_EDP => Head_Type'
+ (Head => Head_EDP,
+ HTOTAL => Registers.HTOTAL_EDP,
+ HBLANK => Registers.HBLANK_EDP,
+ HSYNC => Registers.HSYNC_EDP,
+ VTOTAL => Registers.VTOTAL_EDP,
+ VBLANK => Registers.VBLANK_EDP,
+ VSYNC => Registers.VSYNC_EDP,
+ PIPECONF => Registers.PIPE_EDP_CONF,
+ PIPE_DATA_M1 => Registers.PIPE_EDP_DATA_M1,
+ PIPE_DATA_N1 => Registers.PIPE_EDP_DATA_N1,
+ PIPE_LINK_M1 => Registers.PIPE_EDP_LINK_M1,
+ PIPE_LINK_N1 => Registers.PIPE_EDP_LINK_N1,
+ PIPE_DDI_FUNC_CTL => Registers.PIPE_EDP_DDI_FUNC_CTL,
+ PIPE_MSA_MISC => Registers.PIPE_EDP_MSA_MISC),
+ Head_A => Head_Type'
+ (Head => Head_A,
+ HTOTAL => Registers.HTOTAL_A,
+ HBLANK => Registers.HBLANK_A,
+ HSYNC => Registers.HSYNC_A,
+ VTOTAL => Registers.VTOTAL_A,
+ VBLANK => Registers.VBLANK_A,
+ VSYNC => Registers.VSYNC_A,
+ PIPECONF => Registers.PIPEACONF,
+ PIPE_DATA_M1 => Registers.PIPEA_DATA_M1,
+ PIPE_DATA_N1 => Registers.PIPEA_DATA_N1,
+ PIPE_LINK_M1 => Registers.PIPEA_LINK_M1,
+ PIPE_LINK_N1 => Registers.PIPEA_LINK_N1,
+ PIPE_DDI_FUNC_CTL => Registers.PIPEA_DDI_FUNC_CTL,
+ PIPE_MSA_MISC => Registers.PIPEA_MSA_MISC),
+ Head_B => Head_Type'
+ (Head => Head_B,
+ HTOTAL => Registers.HTOTAL_B,
+ HBLANK => Registers.HBLANK_B,
+ HSYNC => Registers.HSYNC_B,
+ VTOTAL => Registers.VTOTAL_B,
+ VBLANK => Registers.VBLANK_B,
+ VSYNC => Registers.VSYNC_B,
+ PIPECONF => Registers.PIPEBCONF,
+ PIPE_DATA_M1 => Registers.PIPEB_DATA_M1,
+ PIPE_DATA_N1 => Registers.PIPEB_DATA_N1,
+ PIPE_LINK_M1 => Registers.PIPEB_LINK_M1,
+ PIPE_LINK_N1 => Registers.PIPEB_LINK_N1,
+ PIPE_DDI_FUNC_CTL => Registers.PIPEB_DDI_FUNC_CTL,
+ PIPE_MSA_MISC => Registers.PIPEB_MSA_MISC),
+ Head_C => Head_Type'
+ (Head => Head_C,
+ HTOTAL => Registers.HTOTAL_C,
+ HBLANK => Registers.HBLANK_C,
+ HSYNC => Registers.HSYNC_C,
+ VTOTAL => Registers.VTOTAL_C,
+ VBLANK => Registers.VBLANK_C,
+ VSYNC => Registers.VSYNC_C,
+ PIPECONF => Registers.PIPECCONF,
+ PIPE_DATA_M1 => Registers.PIPEC_DATA_M1,
+ PIPE_DATA_N1 => Registers.PIPEC_DATA_N1,
+ PIPE_LINK_M1 => Registers.PIPEC_LINK_M1,
+ PIPE_LINK_N1 => Registers.PIPEC_LINK_N1,
+ PIPE_DDI_FUNC_CTL => Registers.PIPEC_DDI_FUNC_CTL,
+ PIPE_MSA_MISC => Registers.PIPEC_MSA_MISC));
+
+end HW.GFX.GMA.Pipe_Setup;
diff --git a/common/hw-gfx-gma-port_detect.ads b/common/hw-gfx-gma-port_detect.ads
new file mode 100644
index 0000000..848d7ab
--- /dev/null
+++ b/common/hw-gfx-gma-port_detect.ads
@@ -0,0 +1,20 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Port_Detect is
+
+ procedure Initialize;
+
+ procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean);
+
+end HW.GFX.GMA.Port_Detect;
diff --git a/common/hw-gfx-gma-registers.adb b/common/hw-gfx-gma-registers.adb
new file mode 100644
index 0000000..22c5b9b
--- /dev/null
+++ b/common/hw-gfx-gma-registers.adb
@@ -0,0 +1,312 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 System.Storage_Elements;
+
+with HW.Time;
+with HW.MMIO_Range;
+pragma Elaborate_All (HW.MMIO_Range);
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type System.Address;
+use type HW.Word64;
+
+package body HW.GFX.GMA.Registers
+with
+ Refined_State =>
+ (Address_State => (Regs.Base_Address, GTT.Base_Address),
+ Register_State => Regs.State,
+ GTT_State => GTT.State)
+is
+ pragma Disable_Atomic_Synchronization;
+
+ type Registers_Range is
+ new Natural range 0 .. 16#0020_0000# / Register_Width - 1;
+ type Registers_Type is array (Registers_Range) of Word32
+ with
+ Atomic_Components,
+ Size => 16#20_0000# * 8;
+ package Regs is new MMIO_Range
+ (Base_Addr => Config.Default_MMIO_Base,
+ Element_T => Word32,
+ Index_T => Registers_Range,
+ Array_T => Registers_Type);
+
+ ----------------------------------------------------------------------------
+
+ GTT_Offset : constant := (case Config.CPU is
+ when Ironlake .. Haswell => 16#0020_0000#,
+ when Broadwell .. Skylake => 16#0080_0000#);
+
+ GTT_Size : constant := (case Config.CPU is
+ when Ironlake .. Haswell => 16#0020_0000#,
+ -- Limit Broadwell to 4MiB to have a stable
+ -- interface (i.e. same number of entries):
+ when Broadwell .. Skylake => 16#0040_0000#);
+
+ GTT_PTE_Size : constant := (case Config.CPU is
+ when Ironlake .. Haswell => 4,
+ when Broadwell .. Skylake => 8);
+
+
+ type GTT_PTE_Type is mod 2 ** (GTT_PTE_Size * 8);
+ type GTT_Registers_Type is array (GTT_Range) of GTT_PTE_Type
+ with
+ Volatile_Components,
+ Size => GTT_Size * 8;
+ package GTT is new MMIO_Range
+ (Base_Addr => Config.Default_MMIO_Base + GTT_Offset,
+ Element_T => GTT_PTE_Type,
+ Index_T => GTT_Range,
+ Array_T => GTT_Registers_Type);
+
+ GTT_PTE_Valid : constant Word32 := 1;
+
+ ----------------------------------------------------------------------------
+
+ procedure Write_GTT
+ (GTT_Page : GTT_Range;
+ Device_Address : GTT_Address_Type;
+ Valid : Boolean)
+ is
+ begin
+ if Config.Fold_39Bit_GTT_PTE then
+ GTT.Write
+ (Index => GTT_Page,
+ Value => GTT_PTE_Type (Device_Address and 16#ffff_f000#) or
+ GTT_PTE_Type (Shift_Right (Word64 (Device_Address), 32 - 4)
+ and 16#0000_07f0#) or
+ Boolean'Pos (Valid));
+ else
+ GTT.Write
+ (Index => GTT_Page,
+ Value => GTT_PTE_Type (Device_Address and 16#7f_ffff_f000#) or
+ Boolean'Pos (Valid));
+ end if;
+ end Write_GTT;
+
+ ----------------------------------------------------------------------------
+
+ package Rep is
+ function Index (Reg : Registers_Index) return Registers_Range;
+ end Rep;
+
+ package body Rep is
+ function Index (Reg : Registers_Index) return Registers_Range
+ with
+ SPARK_Mode => Off
+ is
+ begin
+ return Reg'Enum_Rep;
+ end Index;
+ end Rep;
+
+ -- Read a specific register
+ procedure Read
+ (Register : in Registers_Index;
+ Value : out Word32;
+ Verbose : in Boolean := True)
+ is
+ begin
+ Regs.Read (Value, Rep.Index (Register));
+
+ pragma Debug (Verbose, Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+ pragma Debug (Verbose, Debug.Put_Word32 (Value));
+ pragma Debug (Verbose, Debug.Put (" <- "));
+ pragma Debug (Verbose, Debug.Put_Word32 (Register'Enum_Rep * Register_Width));
+ pragma Debug (Verbose, Debug.Put (":"));
+ pragma Debug (Verbose, Debug.Put_Line (Registers_Index'Image (Register)));
+ end Read;
+
+ ----------------------------------------------------------------------------
+
+ -- Read a specific register to post a previous write
+ procedure Posting_Read (Register : Registers_Index)
+ is
+ Discard_Value : Word32;
+ begin
+ pragma Warnings
+ (Off, "unused assignment to ""Discard_Value""",
+ Reason => "Intentional dummy read to affect hardware.");
+
+ Read (Register, Discard_Value);
+
+ pragma Warnings
+ (On, "unused assignment to ""Discard_Value""");
+ end Posting_Read;
+
+ ----------------------------------------------------------------------------
+
+ -- Write a specific register
+ procedure Write (Register : Registers_Index; Value : Word32)
+ is
+ begin
+ pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+ pragma Debug (Debug.Put_Word32 (Value));
+ pragma Debug (Debug.Put (" -> "));
+ pragma Debug (Debug.Put_Word32 (Register'Enum_Rep * Register_Width));
+ pragma Debug (Debug.Put (":"));
+ pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+ Regs.Write (Rep.Index (Register), Value);
+ pragma Debug (Debug.Register_Write_Wait);
+ end Write;
+
+ ----------------------------------------------------------------------------
+
+ -- Check whether all bits in @Register@ indicated by @Mask@ are set
+ procedure Is_Set_Mask
+ (Register : in Registers_Index;
+ Mask : in Word32;
+ Result : out Boolean)
+ is
+ Value : Word32;
+ begin
+ pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+ pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+ Read (Register, Value);
+ Result := (Value and Mask) = Mask;
+
+ end Is_Set_Mask;
+
+ ----------------------------------------------------------------------------
+
+ -- TODO: Should have Success parameter
+ -- Wait for all bits in @Register@ indicated by @Mask@ to be set
+ procedure Wait_Set_Mask
+ (Register : in Registers_Index;
+ Mask : in Word32;
+ TOut_MS : in Natural := Default_Timeout_MS;
+ Verbose : in Boolean := False)
+ is
+ Value : Word32;
+ Timeout : Time.T;
+ Timed_Out : Boolean;
+ begin
+ pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+ pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+ Timeout := Time.MS_From_Now (TOut_MS);
+ loop
+ Timed_Out := Time.Timed_Out (Timeout);
+ Read (Register, Value, Verbose);
+ if (Value and Mask) = Mask then
+ exit;
+ end if;
+ pragma Debug (Timed_Out, Debug.Put (GNAT.Source_Info.Enclosing_Entity));
+ pragma Debug (Timed_Out, Debug.Put_Line (": Timed Out!"));
+ exit when Timed_Out;
+ end loop;
+
+ end Wait_Set_Mask;
+
+ ----------------------------------------------------------------------------
+
+ -- TODO: Should have Success parameter
+ -- Wait for bits in @Register@ indicated by @Mask@ to be clear
+ procedure Wait_Unset_Mask
+ (Register : Registers_Index;
+ Mask : Word32;
+ TOut_MS : in Natural := Default_Timeout_MS;
+ Verbose : in Boolean := False)
+ is
+ Value : Word32;
+ Timeout : Time.T;
+ Timed_Out : Boolean;
+ begin
+ pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+ pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+ Timeout := Time.MS_From_Now (TOut_MS);
+ loop
+ Timed_Out := Time.Timed_Out (Timeout);
+ Read (Register, Value, Verbose);
+ if (Value and Mask) = 0 then
+ exit;
+ end if;
+ pragma Debug (Timed_Out, Debug.Put (GNAT.Source_Info.Enclosing_Entity));
+ pragma Debug (Timed_Out, Debug.Put_Line (": Timed Out!"));
+ exit when Timed_Out;
+ end loop;
+
+ end Wait_Unset_Mask;
+
+ ----------------------------------------------------------------------------
+
+ -- Set bits from @Mask@ in @Register@
+ procedure Set_Mask
+ (Register : Registers_Index;
+ Mask : Word32)
+ is
+ Value : Word32;
+ begin
+ pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+ pragma Debug (Debug.Put_Word32 (Mask));
+ pragma Debug (Debug.Put (" .S "));
+ pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+ Read (Register, Value);
+ Value := Value or Mask;
+ Write (Register, Value);
+ end Set_Mask;
+
+ ----------------------------------------------------------------------------
+
+ -- Mask out @Mask@ in @Register@
+ procedure Unset_Mask
+ (Register : Registers_Index;
+ Mask : Word32)
+ is
+ Value : Word32;
+ begin
+ pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+ pragma Debug (Debug.Put_Word32 (Mask));
+ pragma Debug (Debug.Put (" !S "));
+ pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+ Read (Register, Value);
+ Value := Value and not Mask;
+ Write (Register, Value);
+ end Unset_Mask;
+
+ ----------------------------------------------------------------------------
+
+ -- Mask out @Unset_Mask@ and set @Set_Mask@ in @Register@
+ procedure Unset_And_Set_Mask
+ (Register : Registers_Index;
+ Mask_Unset : Word32;
+ Mask_Set : Word32)
+ is
+ Value : Word32;
+ begin
+ pragma Debug (Debug.Put (GNAT.Source_Info.Enclosing_Entity & ": "));
+ pragma Debug (Debug.Put_Line (Registers_Index'Image (Register)));
+
+ Read (Register, Value);
+ Value := (Value and not Mask_Unset) or Mask_Set;
+ Write (Register, Value);
+ end Unset_And_Set_Mask;
+
+ ----------------------------------------------------------------------------
+
+ procedure Set_Register_Base (Base : Word64)
+ is
+ begin
+ Regs.Set_Base_Address (Base);
+ GTT.Set_Base_Address (Base + GTT_Offset);
+ end Set_Register_Base;
+
+end HW.GFX.GMA.Registers;
diff --git a/common/hw-gfx-gma-registers.ads b/common/hw-gfx-gma-registers.ads
new file mode 100644
index 0000000..a765e2d
--- /dev/null
+++ b/common/hw-gfx-gma-registers.ads
@@ -0,0 +1,1121 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 System;
+with HW.GFX.GMA;
+with HW.GFX.GMA.Config;
+
+private package HW.GFX.GMA.Registers
+with
+ Abstract_State =>
+ ((Address_State with Part_Of => GMA.State),
+ (Register_State with External, Part_Of => GMA.Device_State),
+ (GTT_State with External, Part_Of => GMA.Device_State)),
+ Initializes => Address_State
+is
+ type Registers_Invalid_Index is
+ (Invalid_Register, -- Allow a placeholder when access is not acceptable
+
+ RCS_RING_BUFFER_TAIL,
+ RCS_RING_BUFFER_HEAD,
+ RCS_RING_BUFFER_STRT,
+ RCS_RING_BUFFER_CTL,
+ QUIRK_02084,
+ QUIRK_02090,
+ HWSTAM,
+ MI_MODE,
+ INSTPM,
+ GT_MODE,
+ CACHE_MODE_0,
+ CTX_SIZE,
+ PP_DCLV_HIGH,
+ PP_DCLV_LOW,
+ GFX_MODE,
+ ARB_MODE,
+ HWS_PGA,
+ GAM_ECOCHK,
+ MBCTL,
+ UCGCTL1,
+ UCGCTL2,
+ VCS_RING_BUFFER_TAIL,
+ VCS_RING_BUFFER_HEAD,
+ VCS_RING_BUFFER_STRT,
+ VCS_RING_BUFFER_CTL,
+ SLEEP_PSMI_CONTROL,
+ VCS_HWSTAM,
+ VCS_PP_DCLV_HIGH,
+ VCS_PP_DCLV_LOW,
+ GAC_ECO_BITS,
+ BCS_RING_BUFFER_TAIL,
+ BCS_RING_BUFFER_HEAD,
+ BCS_RING_BUFFER_STRT,
+ BCS_RING_BUFFER_CTL,
+ BCS_HWSTAM,
+ BCS_PP_DCLV_HIGH,
+ BCS_PP_DCLV_LOW,
+ GAB_CTL_REG,
+ VGACNTRL,
+ FUSE_STATUS,
+ QUIRK_42004,
+ DSPCLK_GATE_D,
+ FBA_CFB_BASE,
+ FBC_CTL,
+ IPS_CTL,
+ DEISR,
+ DEIMR,
+ DEIIR,
+ DEIER,
+ GTISR,
+ GTIMR,
+ GTIIR,
+ GTIER,
+ IIR,
+ HOTPLUG_CTL,
+ ARB_CTL,
+ DBUF_CTL,
+ WM_PIPE_A,
+ WM_PIPE_B,
+ WM1_LP_ILK,
+ WM2_LP_ILK,
+ WM3_LP_ILK,
+ WM_PIPE_C,
+ WM_LINETIME_A,
+ WM_LINETIME_B,
+ WM_LINETIME_C,
+ PWR_WELL_CTL_BIOS,
+ PWR_WELL_CTL_DRIVER,
+ PWR_WELL_CTL_KVMR,
+ PWR_WELL_CTL_DEBUG,
+ PWR_WELL_CTL5,
+ PWR_WELL_CTL6,
+ CDCLK_CTL,
+ LCPLL1_CTL,
+ LCPLL2_CTL,
+ SPLL_CTL,
+ WRPLL_CTL_1,
+ WRPLL_CTL_2,
+ PORT_CLK_SEL_DDIA,
+ PORT_CLK_SEL_DDIB,
+ PORT_CLK_SEL_DDIC,
+ PORT_CLK_SEL_DDID,
+ PORT_CLK_SEL_DDIE,
+ TRANSA_CLK_SEL,
+ TRANSB_CLK_SEL,
+ TRANSC_CLK_SEL,
+ NDE_RSTWRN_OPT,
+ BLC_PWM_CPU_CTL2,
+ BLC_PWM_CPU_CTL,
+ HTOTAL_A,
+ HBLANK_A,
+ HSYNC_A,
+ VTOTAL_A,
+ VBLANK_A,
+ VSYNC_A,
+ PIPEASRC,
+ PIPE_VSYNCSHIFT_A,
+ PIPEA_DATA_M1,
+ PIPEA_DATA_N1,
+ PIPEA_LINK_M1,
+ PIPEA_LINK_N1,
+ FDI_TX_CTL_A,
+ PIPEA_DDI_FUNC_CTL,
+ PIPEA_MSA_MISC,
+ SRD_CTL_A,
+ SRD_STATUS_A,
+ HTOTAL_B,
+ HBLANK_B,
+ HSYNC_B,
+ VTOTAL_B,
+ VBLANK_B,
+ VSYNC_B,
+ PIPEBSRC,
+ PIPE_VSYNCSHIFT_B,
+ PIPEB_DATA_M1,
+ PIPEB_DATA_N1,
+ PIPEB_LINK_M1,
+ PIPEB_LINK_N1,
+ FDI_TX_CTL_B,
+ PIPEB_DDI_FUNC_CTL,
+ PIPEB_MSA_MISC,
+ SRD_CTL_B,
+ SRD_STATUS_B,
+ HTOTAL_C,
+ HBLANK_C,
+ HSYNC_C,
+ VTOTAL_C,
+ VBLANK_C,
+ VSYNC_C,
+ PIPECSRC,
+ PIPE_VSYNCSHIFT_C,
+ PIPEC_DATA_M1,
+ PIPEC_DATA_N1,
+ PIPEC_LINK_M1,
+ PIPEC_LINK_N1,
+ FDI_TX_CTL_C,
+ PIPEC_DDI_FUNC_CTL,
+ PIPEC_MSA_MISC,
+ SRD_CTL_C,
+ SRD_STATUS_C,
+ DDI_BUF_CTL_A,
+ DDI_AUX_CTL_A,
+ DDI_AUX_DATA_A_1,
+ DDI_AUX_DATA_A_2,
+ DDI_AUX_DATA_A_3,
+ DDI_AUX_DATA_A_4,
+ DDI_AUX_DATA_A_5,
+ DDI_AUX_MUTEX_A,
+ DP_TP_CTL_A,
+ DDI_BUF_CTL_B,
+ DDI_AUX_CTL_B,
+ DDI_AUX_DATA_B_1,
+ DDI_AUX_DATA_B_2,
+ DDI_AUX_DATA_B_3,
+ DDI_AUX_DATA_B_4,
+ DDI_AUX_DATA_B_5,
+ DDI_AUX_MUTEX_B,
+ DP_TP_CTL_B,
+ DP_TP_STATUS_B,
+ DDI_BUF_CTL_C,
+ DDI_AUX_CTL_C,
+ DDI_AUX_DATA_C_1,
+ DDI_AUX_DATA_C_2,
+ DDI_AUX_DATA_C_3,
+ DDI_AUX_DATA_C_4,
+ DDI_AUX_DATA_C_5,
+ DDI_AUX_MUTEX_C,
+ DP_TP_CTL_C,
+ DP_TP_STATUS_C,
+ DDI_BUF_CTL_D,
+ DDI_AUX_CTL_D,
+ DDI_AUX_DATA_D_1,
+ DDI_AUX_DATA_D_2,
+ DDI_AUX_DATA_D_3,
+ DDI_AUX_DATA_D_4,
+ DDI_AUX_DATA_D_5,
+ DDI_AUX_MUTEX_D,
+ DP_TP_CTL_D,
+ DP_TP_STATUS_D,
+ DDI_BUF_CTL_E,
+ DP_TP_CTL_E,
+ DP_TP_STATUS_E,
+ SRD_CTL,
+ SRD_STATUS,
+ AUD_VID_DID,
+ PFA_WIN_POS,
+ PFA_WIN_SZ,
+ PFA_CTL_1,
+ PS_WIN_POS_1_A,
+ PS_WIN_SZ_1_A,
+ PS_CTRL_1_A,
+ PS_WIN_POS_2_A,
+ PS_WIN_SZ_2_A,
+ PS_CTRL_2_A,
+ PFB_WIN_POS,
+ PFB_WIN_SZ,
+ PFB_CTL_1,
+ PS_WIN_POS_1_B,
+ PS_WIN_SZ_1_B,
+ PS_CTRL_1_B,
+ PS_WIN_POS_2_B,
+ PS_WIN_SZ_2_B,
+ PS_CTRL_2_B,
+ PFC_WIN_POS,
+ PFC_WIN_SZ,
+ PFC_CTL_1,
+ PS_WIN_POS_1_C,
+ PS_WIN_SZ_1_C,
+ PS_CTRL_1_C,
+ DPLL1_CFGR1,
+ DPLL1_CFGR2,
+ DPLL2_CFGR1,
+ DPLL2_CFGR2,
+ DPLL3_CFGR1,
+ DPLL3_CFGR2,
+ DPLL_CTRL1,
+ DPLL_CTRL2,
+ DPLL_STATUS,
+ HTOTAL_EDP,
+ HBLANK_EDP,
+ HSYNC_EDP,
+ VTOTAL_EDP,
+ VBLANK_EDP,
+ VSYNC_EDP,
+ PIPE_EDP_DATA_M1,
+ PIPE_EDP_DATA_N1,
+ PIPE_EDP_LINK_M1,
+ PIPE_EDP_LINK_N1,
+ PIPE_EDP_DDI_FUNC_CTL,
+ PIPE_EDP_MSA_MISC,
+ SRD_CTL_EDP,
+ SRD_STATUS_EDP,
+ PIPE_SCANLINE_A,
+ PIPEACONF,
+ PIPEAMISC,
+ PIPE_FRMCNT_A,
+ DSPACNTR,
+ DSPALINOFF,
+ DSPASTRIDE,
+ PLANE_POS_1_A,
+ PLANE_SIZE_1_A,
+ DSPASURF,
+ DSPATILEOFF,
+ PLANE_WM_1_A_0,
+ PLANE_WM_1_A_1,
+ PLANE_WM_1_A_2,
+ PLANE_WM_1_A_3,
+ PLANE_WM_1_A_4,
+ PLANE_WM_1_A_5,
+ PLANE_WM_1_A_6,
+ PLANE_WM_1_A_7,
+ PLANE_BUF_CFG_1_A,
+ SPACNTR,
+ PIPE_SCANLINE_B,
+ PIPEBCONF,
+ PIPEBMISC,
+ PIPE_FRMCNT_B,
+ DSPBCNTR,
+ DSPBLINOFF,
+ DSPBSTRIDE,
+ PLANE_POS_1_B,
+ PLANE_SIZE_1_B,
+ DSPBSURF,
+ DSPBTILEOFF,
+ PLANE_WM_1_B_0,
+ PLANE_WM_1_B_1,
+ PLANE_WM_1_B_2,
+ PLANE_WM_1_B_3,
+ PLANE_WM_1_B_4,
+ PLANE_WM_1_B_5,
+ PLANE_WM_1_B_6,
+ PLANE_WM_1_B_7,
+ PLANE_BUF_CFG_1_B,
+ SPBCNTR,
+ PIPE_SCANLINE_C,
+ PIPECCONF,
+ PIPECMISC,
+ PIPE_FRMCNT_C,
+ DSPCCNTR,
+ DSPCLINOFF,
+ DSPCSTRIDE,
+ PLANE_POS_1_C,
+ PLANE_SIZE_1_C,
+ DSPCSURF,
+ DSPCTILEOFF,
+ PLANE_WM_1_C_0,
+ PLANE_WM_1_C_1,
+ PLANE_WM_1_C_2,
+ PLANE_WM_1_C_3,
+ PLANE_WM_1_C_4,
+ PLANE_WM_1_C_5,
+ PLANE_WM_1_C_6,
+ PLANE_WM_1_C_7,
+ PLANE_BUF_CFG_1_C,
+ SPCCNTR,
+ PIPE_EDP_CONF,
+ PCH_FDI_CHICKEN_B_C,
+ QUIRK_C2004,
+ SFUSE_STRAP,
+ PCH_DSPCLK_GATE_D,
+ SDEISR,
+ SDEIMR,
+ SDEIIR,
+ SDEIER,
+ SHOTPLUG_CTL,
+ PCH_GMBUS0,
+ PCH_GMBUS1,
+ PCH_GMBUS2,
+ PCH_GMBUS3,
+ PCH_GMBUS4,
+ PCH_GMBUS5,
+ SBI_ADDR,
+ SBI_DATA,
+ SBI_CTL_STAT,
+ PCH_DPLL_A,
+ PCH_DPLL_B,
+ PCH_PIXCLK_GATE,
+ PCH_FPA0,
+ PCH_FPA1,
+ PCH_FPB0,
+ PCH_FPB1,
+ PCH_DREF_CONTROL,
+ RAWCLK_FREQ,
+ PCH_DPLL_SEL,
+ PCH_PP_STATUS,
+ PCH_PP_CONTROL,
+ PCH_PP_ON_DELAYS,
+ PCH_PP_OFF_DELAYS,
+ PCH_PP_DIVISOR,
+ BLC_PWM_PCH_CTL1,
+ BLC_PWM_PCH_CTL2,
+ TRANS_HTOTAL_A,
+ TRANS_HBLANK_A,
+ TRANS_HSYNC_A,
+ TRANS_VTOTAL_A,
+ TRANS_VBLANK_A,
+ TRANS_VSYNC_A,
+ TRANS_VSYNCSHIFT_A,
+ TRANSA_DATA_M1,
+ TRANSA_DATA_N1,
+ TRANSA_DP_LINK_M1,
+ TRANSA_DP_LINK_N1,
+ TRANS_DP_CTL_A,
+ TRANS_HTOTAL_B,
+ TRANS_HBLANK_B,
+ TRANS_HSYNC_B,
+ TRANS_VTOTAL_B,
+ TRANS_VBLANK_B,
+ TRANS_VSYNC_B,
+ TRANS_VSYNCSHIFT_B,
+ TRANSB_DATA_M1,
+ TRANSB_DATA_N1,
+ TRANSB_DP_LINK_M1,
+ TRANSB_DP_LINK_N1,
+ PCH_ADPA,
+ PCH_HDMIB,
+ PCH_HDMIC,
+ PCH_HDMID,
+ PCH_LVDS,
+ TRANS_DP_CTL_B,
+ TRANS_HTOTAL_C,
+ TRANS_HBLANK_C,
+ TRANS_HSYNC_C,
+ TRANS_VTOTAL_C,
+ TRANS_VBLANK_C,
+ TRANS_VSYNC_C,
+ TRANS_VSYNCSHIFT_C,
+ TRANSC_DATA_M1,
+ TRANSC_DATA_N1,
+ TRANSC_DP_LINK_M1,
+ TRANSC_DP_LINK_N1,
+ TRANS_DP_CTL_C,
+ PCH_DP_B,
+ PCH_DP_AUX_CTL_B,
+ PCH_DP_AUX_DATA_B_1,
+ PCH_DP_AUX_DATA_B_2,
+ PCH_DP_AUX_DATA_B_3,
+ PCH_DP_AUX_DATA_B_4,
+ PCH_DP_AUX_DATA_B_5,
+ PCH_DP_C,
+ PCH_DP_AUX_CTL_C,
+ PCH_DP_AUX_DATA_C_1,
+ PCH_DP_AUX_DATA_C_2,
+ PCH_DP_AUX_DATA_C_3,
+ PCH_DP_AUX_DATA_C_4,
+ PCH_DP_AUX_DATA_C_5,
+ PCH_DP_D,
+ PCH_DP_AUX_CTL_D,
+ PCH_DP_AUX_DATA_D_1,
+ PCH_DP_AUX_DATA_D_2,
+ PCH_DP_AUX_DATA_D_3,
+ PCH_DP_AUX_DATA_D_4,
+ PCH_DP_AUX_DATA_D_5,
+ AUD_CONFIG_A,
+ PCH_AUD_VID_DID,
+ AUD_HDMIW_HDMIEDID_A,
+ AUD_CNTL_ST_A,
+ AUD_CNTRL_ST2,
+ AUD_CONFIG_B,
+ AUD_HDMIW_HDMIEDID_B,
+ AUD_CNTL_ST_B,
+ AUD_CONFIG_C,
+ AUD_HDMIW_HDMIEDID_C,
+ AUD_CNTL_ST_C,
+ TRANSACONF,
+ FDI_RXA_CTL,
+ FDI_RX_MISC_A,
+ FDI_RXA_IIR,
+ FDI_RXA_IMR,
+ FDI_RXA_TUSIZE1,
+ QUIRK_F0060,
+ TRANSA_CHICKEN2,
+ TRANSBCONF,
+ FDI_RXB_CTL,
+ FDI_RX_MISC_B,
+ FDI_RXB_IIR,
+ FDI_RXB_IMR,
+ FDI_RXB_TUSIZE1,
+ QUIRK_F1060,
+ TRANSB_CHICKEN2,
+ TRANSCCONF,
+ FDI_RXC_CTL,
+ FDI_RX_MISC_C,
+ FDI_RXC_IIR,
+ FDI_RXC_IMR,
+ FDI_RXC_TUSIZE1,
+ QUIRK_F2060,
+ TRANSC_CHICKEN2,
+ GT_MAILBOX,
+ GT_MAILBOX_DATA,
+ GT_MAILBOX_DATA_1);
+
+ pragma Warnings
+ (GNATprove, Off, "pragma ""KEEP_NAMES"" ignored *(not yet supported)",
+ Reason => "TODO: Should it matter?");
+ pragma Keep_Names (Registers_Invalid_Index);
+ pragma Warnings
+ (GNATprove, On, "pragma ""KEEP_NAMES"" ignored *(not yet supported)");
+
+ Register_Width : constant := 4;
+
+ for Registers_Invalid_Index use
+ (Invalid_Register => 0,
+
+ ---------------------------------------------------------------------------
+ -- Pipe A registers
+ ---------------------------------------------------------------------------
+
+ -- pipe timing registers
+
+ HTOTAL_A => 16#06_0000# / Register_Width,
+ HBLANK_A => 16#06_0004# / Register_Width,
+ HSYNC_A => 16#06_0008# / Register_Width,
+ VTOTAL_A => 16#06_000c# / Register_Width,
+ VBLANK_A => 16#06_0010# / Register_Width,
+ VSYNC_A => 16#06_0014# / Register_Width,
+ PIPEASRC => 16#06_001c# / Register_Width,
+ PIPEACONF => 16#07_0008# / Register_Width,
+ PIPEAMISC => 16#07_0030# / Register_Width,
+ TRANS_HTOTAL_A => 16#0e_0000# / Register_Width,
+ TRANS_HBLANK_A => 16#0e_0004# / Register_Width,
+ TRANS_HSYNC_A => 16#0e_0008# / Register_Width,
+ TRANS_VTOTAL_A => 16#0e_000c# / Register_Width,
+ TRANS_VBLANK_A => 16#0e_0010# / Register_Width,
+ TRANS_VSYNC_A => 16#0e_0014# / Register_Width,
+ TRANSA_DATA_M1 => 16#0e_0030# / Register_Width,
+ TRANSA_DATA_N1 => 16#0e_0034# / Register_Width,
+ TRANSA_DP_LINK_M1 => 16#0e_0040# / Register_Width,
+ TRANSA_DP_LINK_N1 => 16#0e_0044# / Register_Width,
+ PIPEA_DATA_M1 => 16#06_0030# / Register_Width,
+ PIPEA_DATA_N1 => 16#06_0034# / Register_Width,
+ PIPEA_LINK_M1 => 16#06_0040# / Register_Width,
+ PIPEA_LINK_N1 => 16#06_0044# / Register_Width,
+ PIPEA_DDI_FUNC_CTL => 16#06_0400# / Register_Width,
+ PIPEA_MSA_MISC => 16#06_0410# / Register_Width,
+
+ -- PCH sideband interface registers
+ SBI_ADDR => 16#0c_6000# / Register_Width,
+ SBI_DATA => 16#0c_6004# / Register_Width,
+ SBI_CTL_STAT => 16#0c_6008# / Register_Width,
+
+ -- clock registers
+ PCH_DPLL_A => 16#0c_6014# / Register_Width,
+ PCH_PIXCLK_GATE => 16#0c_6020# / Register_Width,
+ PCH_FPA0 => 16#0c_6040# / Register_Width,
+ PCH_FPA1 => 16#0c_6044# / Register_Width,
+
+ -- panel fitter
+ PFA_CTL_1 => 16#06_8080# / Register_Width,
+ PFA_WIN_POS => 16#06_8070# / Register_Width,
+ PFA_WIN_SZ => 16#06_8074# / Register_Width,
+ PS_WIN_POS_1_A => 16#06_8170# / Register_Width,
+ PS_WIN_SZ_1_A => 16#06_8174# / Register_Width,
+ PS_CTRL_1_A => 16#06_8180# / Register_Width,
+ PS_WIN_POS_2_A => 16#06_8270# / Register_Width,
+ PS_WIN_SZ_2_A => 16#06_8274# / Register_Width,
+ PS_CTRL_2_A => 16#06_8280# / Register_Width,
+
+ -- display control
+ DSPACNTR => 16#07_0180# / Register_Width,
+ DSPALINOFF => 16#07_0184# / Register_Width,
+ DSPASTRIDE => 16#07_0188# / Register_Width,
+ PLANE_POS_1_A => 16#07_018c# / Register_Width,
+ PLANE_SIZE_1_A => 16#07_0190# / Register_Width,
+ DSPASURF => 16#07_019c# / Register_Width,
+ DSPATILEOFF => 16#07_01a4# / Register_Width,
+
+ -- sprite control
+ SPACNTR => 16#07_0280# / Register_Width,
+
+ -- FDI and PCH transcoder control
+ FDI_TX_CTL_A => 16#06_0100# / Register_Width,
+ FDI_RXA_CTL => 16#0f_000c# / Register_Width,
+ FDI_RX_MISC_A => 16#0f_0010# / Register_Width,
+ FDI_RXA_IIR => 16#0f_0014# / Register_Width,
+ FDI_RXA_IMR => 16#0f_0018# / Register_Width,
+ FDI_RXA_TUSIZE1 => 16#0f_0030# / Register_Width,
+ TRANSACONF => 16#0f_0008# / Register_Width,
+ TRANSA_CHICKEN2 => 16#0f_0064# / Register_Width,
+
+ -- watermark registers
+ WM_LINETIME_A => 16#04_5270# / Register_Width,
+ PLANE_WM_1_A_0 => 16#07_0240# / Register_Width,
+ PLANE_WM_1_A_1 => 16#07_0244# / Register_Width,
+ PLANE_WM_1_A_2 => 16#07_0248# / Register_Width,
+ PLANE_WM_1_A_3 => 16#07_024c# / Register_Width,
+ PLANE_WM_1_A_4 => 16#07_0250# / Register_Width,
+ PLANE_WM_1_A_5 => 16#07_0254# / Register_Width,
+ PLANE_WM_1_A_6 => 16#07_0258# / Register_Width,
+ PLANE_WM_1_A_7 => 16#07_025c# / Register_Width,
+ PLANE_BUF_CFG_1_A => 16#07_027c# / Register_Width,
+
+ -- CPU transcoder clock select
+ TRANSA_CLK_SEL => 16#04_6140# / Register_Width,
+
+ ---------------------------------------------------------------------------
+ -- Pipe B registers
+ ---------------------------------------------------------------------------
+
+ -- pipe timing registers
+
+ HTOTAL_B => 16#06_1000# / Register_Width,
+ HBLANK_B => 16#06_1004# / Register_Width,
+ HSYNC_B => 16#06_1008# / Register_Width,
+ VTOTAL_B => 16#06_100c# / Register_Width,
+ VBLANK_B => 16#06_1010# / Register_Width,
+ VSYNC_B => 16#06_1014# / Register_Width,
+ PIPEBSRC => 16#06_101c# / Register_Width,
+ PIPEBCONF => 16#07_1008# / Register_Width,
+ PIPEBMISC => 16#07_1030# / Register_Width,
+ TRANS_HTOTAL_B => 16#0e_1000# / Register_Width,
+ TRANS_HBLANK_B => 16#0e_1004# / Register_Width,
+ TRANS_HSYNC_B => 16#0e_1008# / Register_Width,
+ TRANS_VTOTAL_B => 16#0e_100c# / Register_Width,
+ TRANS_VBLANK_B => 16#0e_1010# / Register_Width,
+ TRANS_VSYNC_B => 16#0e_1014# / Register_Width,
+ TRANSB_DATA_M1 => 16#0e_1030# / Register_Width,
+ TRANSB_DATA_N1 => 16#0e_1034# / Register_Width,
+ TRANSB_DP_LINK_M1 => 16#0e_1040# / Register_Width,
+ TRANSB_DP_LINK_N1 => 16#0e_1044# / Register_Width,
+ PIPEB_DATA_M1 => 16#06_1030# / Register_Width,
+ PIPEB_DATA_N1 => 16#06_1034# / Register_Width,
+ PIPEB_LINK_M1 => 16#06_1040# / Register_Width,
+ PIPEB_LINK_N1 => 16#06_1044# / Register_Width,
+ PIPEB_DDI_FUNC_CTL => 16#06_1400# / Register_Width,
+ PIPEB_MSA_MISC => 16#06_1410# / Register_Width,
+
+ -- clock registers
+ PCH_DPLL_B => 16#0c_6018# / Register_Width,
+ PCH_FPB0 => 16#0c_6048# / Register_Width,
+ PCH_FPB1 => 16#0c_604c# / Register_Width,
+
+ -- panel fitter
+ PFB_CTL_1 => 16#06_8880# / Register_Width,
+ PFB_WIN_POS => 16#06_8870# / Register_Width,
+ PFB_WIN_SZ => 16#06_8874# / Register_Width,
+ PS_WIN_POS_1_B => 16#06_8970# / Register_Width,
+ PS_WIN_SZ_1_B => 16#06_8974# / Register_Width,
+ PS_CTRL_1_B => 16#06_8980# / Register_Width,
+ PS_WIN_POS_2_B => 16#06_8a70# / Register_Width,
+ PS_WIN_SZ_2_B => 16#06_8a74# / Register_Width,
+ PS_CTRL_2_B => 16#06_8a80# / Register_Width,
+
+ -- display control
+ DSPBCNTR => 16#07_1180# / Register_Width,
+ DSPBLINOFF => 16#07_1184# / Register_Width,
+ DSPBSTRIDE => 16#07_1188# / Register_Width,
+ PLANE_POS_1_B => 16#07_118c# / Register_Width,
+ PLANE_SIZE_1_B => 16#07_1190# / Register_Width,
+ DSPBSURF => 16#07_119c# / Register_Width,
+ DSPBTILEOFF => 16#07_11a4# / Register_Width,
+
+ -- sprite control
+ SPBCNTR => 16#07_1280# / Register_Width,
+
+ -- FDI and PCH transcoder control
+ FDI_TX_CTL_B => 16#06_1100# / Register_Width,
+ FDI_RXB_CTL => 16#0f_100c# / Register_Width,
+ FDI_RX_MISC_B => 16#0f_1010# / Register_Width,
+ FDI_RXB_IIR => 16#0f_1014# / Register_Width,
+ FDI_RXB_IMR => 16#0f_1018# / Register_Width,
+ FDI_RXB_TUSIZE1 => 16#0f_1030# / Register_Width,
+ TRANSBCONF => 16#0f_1008# / Register_Width,
+ TRANSB_CHICKEN2 => 16#0f_1064# / Register_Width,
+
+ -- watermark registers
+ WM_LINETIME_B => 16#04_5274# / Register_Width,
+ PLANE_WM_1_B_0 => 16#07_1240# / Register_Width,
+ PLANE_WM_1_B_1 => 16#07_1244# / Register_Width,
+ PLANE_WM_1_B_2 => 16#07_1248# / Register_Width,
+ PLANE_WM_1_B_3 => 16#07_124c# / Register_Width,
+ PLANE_WM_1_B_4 => 16#07_1250# / Register_Width,
+ PLANE_WM_1_B_5 => 16#07_1254# / Register_Width,
+ PLANE_WM_1_B_6 => 16#07_1258# / Register_Width,
+ PLANE_WM_1_B_7 => 16#07_125c# / Register_Width,
+ PLANE_BUF_CFG_1_B => 16#07_127c# / Register_Width,
+
+ -- CPU transcoder clock select
+ TRANSB_CLK_SEL => 16#04_6144# / Register_Width,
+
+ ---------------------------------------------------------------------------
+ -- Pipe C registers
+ ---------------------------------------------------------------------------
+
+ -- pipe timing registers
+
+ HTOTAL_C => 16#06_2000# / Register_Width,
+ HBLANK_C => 16#06_2004# / Register_Width,
+ HSYNC_C => 16#06_2008# / Register_Width,
+ VTOTAL_C => 16#06_200c# / Register_Width,
+ VBLANK_C => 16#06_2010# / Register_Width,
+ VSYNC_C => 16#06_2014# / Register_Width,
+ PIPECSRC => 16#06_201c# / Register_Width,
+ PIPECCONF => 16#07_2008# / Register_Width,
+ PIPECMISC => 16#07_2030# / Register_Width,
+ TRANS_HTOTAL_C => 16#0e_2000# / Register_Width,
+ TRANS_HBLANK_C => 16#0e_2004# / Register_Width,
+ TRANS_HSYNC_C => 16#0e_2008# / Register_Width,
+ TRANS_VTOTAL_C => 16#0e_200c# / Register_Width,
+ TRANS_VBLANK_C => 16#0e_2010# / Register_Width,
+ TRANS_VSYNC_C => 16#0e_2014# / Register_Width,
+ TRANSC_DATA_M1 => 16#0e_2030# / Register_Width,
+ TRANSC_DATA_N1 => 16#0e_2034# / Register_Width,
+ TRANSC_DP_LINK_M1 => 16#0e_2040# / Register_Width,
+ TRANSC_DP_LINK_N1 => 16#0e_2044# / Register_Width,
+ PIPEC_DATA_M1 => 16#06_2030# / Register_Width,
+ PIPEC_DATA_N1 => 16#06_2034# / Register_Width,
+ PIPEC_LINK_M1 => 16#06_2040# / Register_Width,
+ PIPEC_LINK_N1 => 16#06_2044# / Register_Width,
+ PIPEC_DDI_FUNC_CTL => 16#06_2400# / Register_Width,
+ PIPEC_MSA_MISC => 16#06_2410# / Register_Width,
+
+ -- panel fitter
+ PFC_CTL_1 => 16#06_9080# / Register_Width,
+ PFC_WIN_POS => 16#06_9070# / Register_Width,
+ PFC_WIN_SZ => 16#06_9074# / Register_Width,
+ PS_WIN_POS_1_C => 16#06_9170# / Register_Width,
+ PS_WIN_SZ_1_C => 16#06_9174# / Register_Width,
+ PS_CTRL_1_C => 16#06_9180# / Register_Width,
+
+ -- display control
+ DSPCCNTR => 16#07_2180# / Register_Width,
+ DSPCLINOFF => 16#07_2184# / Register_Width,
+ DSPCSTRIDE => 16#07_2188# / Register_Width,
+ PLANE_POS_1_C => 16#07_218c# / Register_Width,
+ PLANE_SIZE_1_C => 16#07_2190# / Register_Width,
+ DSPCSURF => 16#07_219c# / Register_Width,
+ DSPCTILEOFF => 16#07_21a4# / Register_Width,
+
+ -- sprite control
+ SPCCNTR => 16#07_2280# / Register_Width,
+
+ -- PCH transcoder control
+ FDI_TX_CTL_C => 16#06_2100# / Register_Width,
+ FDI_RXC_CTL => 16#0f_200c# / Register_Width,
+ FDI_RX_MISC_C => 16#0f_2010# / Register_Width,
+ FDI_RXC_IIR => 16#0f_2014# / Register_Width,
+ FDI_RXC_IMR => 16#0f_2018# / Register_Width,
+ FDI_RXC_TUSIZE1 => 16#0f_2030# / Register_Width,
+ TRANSCCONF => 16#0f_2008# / Register_Width,
+ TRANSC_CHICKEN2 => 16#0f_2064# / Register_Width,
+
+ -- watermark registers
+ WM_LINETIME_C => 16#04_5278# / Register_Width,
+ PLANE_WM_1_C_0 => 16#07_2240# / Register_Width,
+ PLANE_WM_1_C_1 => 16#07_2244# / Register_Width,
+ PLANE_WM_1_C_2 => 16#07_2248# / Register_Width,
+ PLANE_WM_1_C_3 => 16#07_224c# / Register_Width,
+ PLANE_WM_1_C_4 => 16#07_2250# / Register_Width,
+ PLANE_WM_1_C_5 => 16#07_2254# / Register_Width,
+ PLANE_WM_1_C_6 => 16#07_2258# / Register_Width,
+ PLANE_WM_1_C_7 => 16#07_225c# / Register_Width,
+ PLANE_BUF_CFG_1_C => 16#07_227c# / Register_Width,
+
+ -- CPU transcoder clock select
+ TRANSC_CLK_SEL => 16#04_6148# / Register_Width,
+
+ ---------------------------------------------------------------------------
+ -- Pipe EDP registers
+ ---------------------------------------------------------------------------
+
+ -- pipe timing registers
+
+ HTOTAL_EDP => 16#06_f000# / Register_Width,
+ HBLANK_EDP => 16#06_f004# / Register_Width,
+ HSYNC_EDP => 16#06_f008# / Register_Width,
+ VTOTAL_EDP => 16#06_f00c# / Register_Width,
+ VBLANK_EDP => 16#06_f010# / Register_Width,
+ VSYNC_EDP => 16#06_f014# / Register_Width,
+ PIPE_EDP_CONF => 16#07_f008# / Register_Width,
+ PIPE_EDP_DATA_M1 => 16#06_f030# / Register_Width,
+ PIPE_EDP_DATA_N1 => 16#06_f034# / Register_Width,
+ PIPE_EDP_LINK_M1 => 16#06_f040# / Register_Width,
+ PIPE_EDP_LINK_N1 => 16#06_f044# / Register_Width,
+ PIPE_EDP_DDI_FUNC_CTL => 16#06_f400# / Register_Width,
+ PIPE_EDP_MSA_MISC => 16#06_f410# / Register_Width,
+
+ -- PSR registers
+ SRD_CTL => 16#06_4800# / Register_Width,
+ SRD_CTL_A => 16#06_0800# / Register_Width,
+ SRD_CTL_B => 16#06_1800# / Register_Width,
+ SRD_CTL_C => 16#06_2800# / Register_Width,
+ SRD_CTL_EDP => 16#06_f800# / Register_Width,
+ SRD_STATUS => 16#06_4840# / Register_Width,
+ SRD_STATUS_A => 16#06_0840# / Register_Width,
+ SRD_STATUS_B => 16#06_1840# / Register_Width,
+ SRD_STATUS_C => 16#06_2840# / Register_Width,
+ SRD_STATUS_EDP => 16#06_f840# / Register_Width,
+
+ -- DDI registers
+ DDI_BUF_CTL_A => 16#06_4000# / Register_Width, -- aliased by DP_CTL_A
+ DDI_AUX_CTL_A => 16#06_4010# / Register_Width, -- aliased by DP_AUX_CTL_A
+ DDI_AUX_DATA_A_1 => 16#06_4014# / Register_Width, -- aliased by DP_AUX_DATA_A_1
+ DDI_AUX_DATA_A_2 => 16#06_4018# / Register_Width, -- aliased by DP_AUX_DATA_A_2
+ DDI_AUX_DATA_A_3 => 16#06_401c# / Register_Width, -- aliased by DP_AUX_DATA_A_3
+ DDI_AUX_DATA_A_4 => 16#06_4020# / Register_Width, -- aliased by DP_AUX_DATA_A_4
+ DDI_AUX_DATA_A_5 => 16#06_4024# / Register_Width, -- aliased by DP_AUX_DATA_A_5
+ DDI_AUX_MUTEX_A => 16#06_402c# / Register_Width,
+ DDI_BUF_CTL_B => 16#06_4100# / Register_Width,
+ DDI_AUX_CTL_B => 16#06_4110# / Register_Width,
+ DDI_AUX_DATA_B_1 => 16#06_4114# / Register_Width,
+ DDI_AUX_DATA_B_2 => 16#06_4118# / Register_Width,
+ DDI_AUX_DATA_B_3 => 16#06_411c# / Register_Width,
+ DDI_AUX_DATA_B_4 => 16#06_4120# / Register_Width,
+ DDI_AUX_DATA_B_5 => 16#06_4124# / Register_Width,
+ DDI_AUX_MUTEX_B => 16#06_412c# / Register_Width,
+ DDI_BUF_CTL_C => 16#06_4200# / Register_Width,
+ DDI_AUX_CTL_C => 16#06_4210# / Register_Width,
+ DDI_AUX_DATA_C_1 => 16#06_4214# / Register_Width,
+ DDI_AUX_DATA_C_2 => 16#06_4218# / Register_Width,
+ DDI_AUX_DATA_C_3 => 16#06_421c# / Register_Width,
+ DDI_AUX_DATA_C_4 => 16#06_4220# / Register_Width,
+ DDI_AUX_DATA_C_5 => 16#06_4224# / Register_Width,
+ DDI_AUX_MUTEX_C => 16#06_422c# / Register_Width,
+ DDI_BUF_CTL_D => 16#06_4300# / Register_Width,
+ DDI_AUX_CTL_D => 16#06_4310# / Register_Width,
+ DDI_AUX_DATA_D_1 => 16#06_4314# / Register_Width,
+ DDI_AUX_DATA_D_2 => 16#06_4318# / Register_Width,
+ DDI_AUX_DATA_D_3 => 16#06_431c# / Register_Width,
+ DDI_AUX_DATA_D_4 => 16#06_4320# / Register_Width,
+ DDI_AUX_DATA_D_5 => 16#06_4324# / Register_Width,
+ DDI_AUX_MUTEX_D => 16#06_432c# / Register_Width,
+ DDI_BUF_CTL_E => 16#06_4400# / Register_Width,
+ DP_TP_CTL_A => 16#06_4040# / Register_Width,
+ DP_TP_CTL_B => 16#06_4140# / Register_Width,
+ DP_TP_CTL_C => 16#06_4240# / Register_Width,
+ DP_TP_CTL_D => 16#06_4340# / Register_Width,
+ DP_TP_CTL_E => 16#06_4440# / Register_Width,
+ DP_TP_STATUS_B => 16#06_4144# / Register_Width,
+ DP_TP_STATUS_C => 16#06_4244# / Register_Width,
+ DP_TP_STATUS_D => 16#06_4344# / Register_Width,
+ DP_TP_STATUS_E => 16#06_4444# / Register_Width,
+ PORT_CLK_SEL_DDIA => 16#04_6100# / Register_Width,
+ PORT_CLK_SEL_DDIB => 16#04_6104# / Register_Width,
+ PORT_CLK_SEL_DDIC => 16#04_6108# / Register_Width,
+ PORT_CLK_SEL_DDID => 16#04_610c# / Register_Width,
+ PORT_CLK_SEL_DDIE => 16#04_6110# / Register_Width,
+
+ -- Skylake DPLL registers
+ DPLL1_CFGR1 => 16#06_c040# / Register_Width,
+ DPLL1_CFGR2 => 16#06_c044# / Register_Width,
+ DPLL2_CFGR1 => 16#06_c048# / Register_Width,
+ DPLL2_CFGR2 => 16#06_c04c# / Register_Width,
+ DPLL3_CFGR1 => 16#06_c050# / Register_Width,
+ DPLL3_CFGR2 => 16#06_c054# / Register_Width,
+ DPLL_CTRL1 => 16#06_c058# / Register_Width,
+ DPLL_CTRL2 => 16#06_c05c# / Register_Width,
+ DPLL_STATUS => 16#06_c060# / Register_Width,
+
+ -- CD CLK register
+ CDCLK_CTL => 16#04_6000# / Register_Width,
+
+ -- Skylake LCPLL registers
+ LCPLL1_CTL => 16#04_6010# / Register_Width,
+ LCPLL2_CTL => 16#04_6014# / Register_Width,
+
+ -- SPLL register
+ SPLL_CTL => 16#04_6020# / Register_Width,
+
+ -- WRPLL registers
+ WRPLL_CTL_1 => 16#04_6040# / Register_Width,
+ WRPLL_CTL_2 => 16#04_6060# / Register_Width,
+
+ -- Power Down Well registers
+ PWR_WELL_CTL_BIOS => 16#04_5400# / Register_Width,
+ PWR_WELL_CTL_DRIVER => 16#04_5404# / Register_Width,
+ PWR_WELL_CTL_KVMR => 16#04_5408# / Register_Width,
+ PWR_WELL_CTL_DEBUG => 16#04_540c# / Register_Width,
+ PWR_WELL_CTL5 => 16#04_5410# / Register_Width,
+ PWR_WELL_CTL6 => 16#04_5414# / Register_Width,
+
+ -- class Panel registers
+ PCH_PP_STATUS => 16#0c_7200# / Register_Width,
+ PCH_PP_CONTROL => 16#0c_7204# / Register_Width,
+ PCH_PP_ON_DELAYS => 16#0c_7208# / Register_Width,
+ PCH_PP_OFF_DELAYS => 16#0c_720c# / Register_Width,
+ PCH_PP_DIVISOR => 16#0c_7210# / Register_Width,
+ BLC_PWM_CPU_CTL => 16#04_8254# / Register_Width,
+ BLC_PWM_PCH_CTL2 => 16#0c_8254# / Register_Width,
+
+ -- PCH LVDS Connector Registers
+ PCH_LVDS => 16#0e_1180# / Register_Width,
+
+ -- PCH ADPA Connector Registers
+ PCH_ADPA => 16#0e_1100# / Register_Width,
+
+ -- PCH HDMIB Connector Registers
+ PCH_HDMIB => 16#0e_1140# / Register_Width,
+
+ -- PCH HDMIC Connector Registers
+ PCH_HDMIC => 16#0e_1150# / Register_Width,
+
+ -- PCH HDMID Connector Registers
+ PCH_HDMID => 16#0e_1160# / Register_Width,
+
+ -- Intel Registers
+ VGACNTRL => 16#04_1000# / Register_Width,
+ FUSE_STATUS => 16#04_2000# / Register_Width,
+ FBA_CFB_BASE => 16#04_3200# / Register_Width,
+ IPS_CTL => 16#04_3408# / Register_Width,
+ ARB_CTL => 16#04_5000# / Register_Width,
+ DBUF_CTL => 16#04_5008# / Register_Width,
+ NDE_RSTWRN_OPT => 16#04_6408# / Register_Width,
+ PCH_DREF_CONTROL => 16#0c_6200# / Register_Width,
+ BLC_PWM_PCH_CTL1 => 16#0c_8250# / Register_Width,
+ BLC_PWM_CPU_CTL2 => 16#04_8250# / Register_Width,
+ PCH_DPLL_SEL => 16#0c_7000# / Register_Width,
+ GT_MAILBOX => 16#13_8124# / Register_Width,
+ GT_MAILBOX_DATA => 16#13_8128# / Register_Width,
+ GT_MAILBOX_DATA_1 => 16#13_812c# / Register_Width,
+
+ PCH_DP_B => 16#0e_4100# / Register_Width,
+ PCH_DP_AUX_CTL_B => 16#0e_4110# / Register_Width,
+ PCH_DP_AUX_DATA_B_1 => 16#0e_4114# / Register_Width,
+ PCH_DP_AUX_DATA_B_2 => 16#0e_4118# / Register_Width,
+ PCH_DP_AUX_DATA_B_3 => 16#0e_411c# / Register_Width,
+ PCH_DP_AUX_DATA_B_4 => 16#0e_4120# / Register_Width,
+ PCH_DP_AUX_DATA_B_5 => 16#0e_4124# / Register_Width,
+ PCH_DP_C => 16#0e_4200# / Register_Width,
+ PCH_DP_AUX_CTL_C => 16#0e_4210# / Register_Width,
+ PCH_DP_AUX_DATA_C_1 => 16#0e_4214# / Register_Width,
+ PCH_DP_AUX_DATA_C_2 => 16#0e_4218# / Register_Width,
+ PCH_DP_AUX_DATA_C_3 => 16#0e_421c# / Register_Width,
+ PCH_DP_AUX_DATA_C_4 => 16#0e_4220# / Register_Width,
+ PCH_DP_AUX_DATA_C_5 => 16#0e_4224# / Register_Width,
+ PCH_DP_D => 16#0e_4300# / Register_Width,
+ PCH_DP_AUX_CTL_D => 16#0e_4310# / Register_Width,
+ PCH_DP_AUX_DATA_D_1 => 16#0e_4314# / Register_Width,
+ PCH_DP_AUX_DATA_D_2 => 16#0e_4318# / Register_Width,
+ PCH_DP_AUX_DATA_D_3 => 16#0e_431c# / Register_Width,
+ PCH_DP_AUX_DATA_D_4 => 16#0e_4320# / Register_Width,
+ PCH_DP_AUX_DATA_D_5 => 16#0e_4324# / Register_Width,
+
+ -- watermark registers
+ WM1_LP_ILK => 16#04_5108# / Register_Width,
+ WM2_LP_ILK => 16#04_510c# / Register_Width,
+ WM3_LP_ILK => 16#04_5110# / Register_Width,
+
+ -- audio VID/DID
+ AUD_VID_DID => 16#06_5020# / Register_Width,
+ PCH_AUD_VID_DID => 16#0e_5020# / Register_Width,
+
+ -- interrupt registers
+ DEISR => 16#04_4000# / Register_Width,
+ DEIMR => 16#04_4004# / Register_Width,
+ DEIIR => 16#04_4008# / Register_Width,
+ DEIER => 16#04_400c# / Register_Width,
+ GTISR => 16#04_4010# / Register_Width,
+ GTIMR => 16#04_4014# / Register_Width,
+ GTIIR => 16#04_4018# / Register_Width,
+ GTIER => 16#04_401c# / Register_Width,
+ SDEISR => 16#0c_4000# / Register_Width,
+ SDEIMR => 16#0c_4004# / Register_Width,
+ SDEIIR => 16#0c_4008# / Register_Width,
+ SDEIER => 16#0c_400c# / Register_Width,
+
+ -- I2C stuff
+ PCH_GMBUS0 => 16#0c_5100# / Register_Width,
+ PCH_GMBUS1 => 16#0c_5104# / Register_Width,
+ PCH_GMBUS2 => 16#0c_5108# / Register_Width,
+ PCH_GMBUS3 => 16#0c_510c# / Register_Width,
+ PCH_GMBUS4 => 16#0c_5110# / Register_Width,
+ PCH_GMBUS5 => 16#0c_5120# / Register_Width,
+
+ -- clock gating -- maybe have to touch this
+ DSPCLK_GATE_D => 16#04_2020# / Register_Width,
+ PCH_FDI_CHICKEN_B_C => 16#0c_2000# / Register_Width,
+ PCH_DSPCLK_GATE_D => 16#0c_2020# / Register_Width,
+
+ -- hotplug and initial detection
+ HOTPLUG_CTL => 16#04_4030# / Register_Width,
+ SHOTPLUG_CTL => 16#0c_4030# / Register_Width,
+ SFUSE_STRAP => 16#0c_2014# / Register_Width,
+
+ -- Render Engine Command Streamer
+ ARB_MODE => 16#00_4030# / Register_Width,
+ HWS_PGA => 16#00_4080# / Register_Width,
+ RCS_RING_BUFFER_TAIL => 16#00_2030# / Register_Width,
+ VCS_RING_BUFFER_TAIL => 16#01_2030# / Register_Width,
+ BCS_RING_BUFFER_TAIL => 16#02_2030# / Register_Width,
+ RCS_RING_BUFFER_HEAD => 16#00_2034# / Register_Width,
+ VCS_RING_BUFFER_HEAD => 16#01_2034# / Register_Width,
+ BCS_RING_BUFFER_HEAD => 16#02_2034# / Register_Width,
+ RCS_RING_BUFFER_STRT => 16#00_2038# / Register_Width,
+ VCS_RING_BUFFER_STRT => 16#01_2038# / Register_Width,
+ BCS_RING_BUFFER_STRT => 16#02_2038# / Register_Width,
+ RCS_RING_BUFFER_CTL => 16#00_203c# / Register_Width,
+ VCS_RING_BUFFER_CTL => 16#01_203c# / Register_Width,
+ BCS_RING_BUFFER_CTL => 16#02_203c# / Register_Width,
+ MI_MODE => 16#00_209c# / Register_Width,
+ INSTPM => 16#00_20c0# / Register_Width,
+ GAB_CTL_REG => 16#02_4000# / Register_Width,
+ PP_DCLV_HIGH => 16#00_2220# / Register_Width,
+ PP_DCLV_LOW => 16#00_2228# / Register_Width,
+ VCS_PP_DCLV_HIGH => 16#01_2220# / Register_Width,
+ VCS_PP_DCLV_LOW => 16#01_2228# / Register_Width,
+ BCS_PP_DCLV_HIGH => 16#02_2220# / Register_Width,
+ BCS_PP_DCLV_LOW => 16#02_2228# / Register_Width,
+ QUIRK_42004 => 16#04_2004# / Register_Width,
+ UCGCTL1 => 16#00_9400# / Register_Width,
+ UCGCTL2 => 16#00_9404# / Register_Width,
+ MBCTL => 16#00_907c# / Register_Width,
+ HWSTAM => 16#00_2098# / Register_Width,
+ VCS_HWSTAM => 16#01_2098# / Register_Width,
+ BCS_HWSTAM => 16#02_2098# / Register_Width,
+ IIR => 16#04_4028# / Register_Width,
+ PIPE_FRMCNT_A => 16#07_0040# / Register_Width,
+ PIPE_FRMCNT_B => 16#07_1040# / Register_Width,
+ PIPE_FRMCNT_C => 16#07_2040# / Register_Width,
+ FBC_CTL => 16#04_3208# / Register_Width,
+ PIPE_VSYNCSHIFT_A => 16#06_0028# / Register_Width,
+ PIPE_VSYNCSHIFT_B => 16#06_1028# / Register_Width,
+ PIPE_VSYNCSHIFT_C => 16#06_2028# / Register_Width,
+ WM_PIPE_A => 16#04_5100# / Register_Width,
+ WM_PIPE_B => 16#04_5104# / Register_Width,
+ WM_PIPE_C => 16#04_5200# / Register_Width,
+ PIPE_SCANLINE_A => 16#07_0000# / Register_Width,
+ PIPE_SCANLINE_B => 16#07_1000# / Register_Width,
+ PIPE_SCANLINE_C => 16#07_2000# / Register_Width,
+ GFX_MODE => 16#00_2520# / Register_Width,
+ CACHE_MODE_0 => 16#00_2120# / Register_Width,
+ SLEEP_PSMI_CONTROL => 16#01_2050# / Register_Width,
+ CTX_SIZE => 16#00_21a0# / Register_Width,
+ GAC_ECO_BITS => 16#01_4090# / Register_Width,
+ GAM_ECOCHK => 16#00_4090# / Register_Width,
+ QUIRK_02084 => 16#00_2084# / Register_Width,
+ QUIRK_02090 => 16#00_2090# / Register_Width,
+ GT_MODE => 16#00_20d0# / Register_Width,
+ QUIRK_F0060 => 16#0f_0060# / Register_Width,
+ QUIRK_F1060 => 16#0f_1060# / Register_Width,
+ QUIRK_F2060 => 16#0f_2060# / Register_Width,
+ AUD_CNTRL_ST2 => 16#0e_50c0# / Register_Width,
+ AUD_CNTL_ST_A => 16#0e_50b4# / Register_Width,
+ AUD_CNTL_ST_B => 16#0e_51b4# / Register_Width,
+ AUD_CNTL_ST_C => 16#0e_52b4# / Register_Width,
+ AUD_HDMIW_HDMIEDID_A => 16#0e_5050# / Register_Width,
+ AUD_HDMIW_HDMIEDID_B => 16#0e_5150# / Register_Width,
+ AUD_HDMIW_HDMIEDID_C => 16#0e_5250# / Register_Width,
+ AUD_CONFIG_A => 16#0e_5000# / Register_Width,
+ AUD_CONFIG_B => 16#0e_5100# / Register_Width,
+ AUD_CONFIG_C => 16#0e_5200# / Register_Width,
+ TRANS_DP_CTL_A => 16#0e_0300# / Register_Width,
+ TRANS_DP_CTL_B => 16#0e_1300# / Register_Width,
+ TRANS_DP_CTL_C => 16#0e_2300# / Register_Width,
+ TRANS_VSYNCSHIFT_A => 16#0e_0028# / Register_Width,
+ TRANS_VSYNCSHIFT_B => 16#0e_1028# / Register_Width,
+ TRANS_VSYNCSHIFT_C => 16#0e_2028# / Register_Width,
+ RAWCLK_FREQ => 16#0c_6204# / Register_Width,
+ QUIRK_C2004 => 16#0c_2004# / Register_Width);
+
+ subtype Registers_Index is Registers_Invalid_Index range
+ Registers_Invalid_Index'Succ (Invalid_Register) ..
+ Registers_Invalid_Index'Last;
+
+ -- aliased registers
+ DP_CTL_A : constant Registers_Index := DDI_BUF_CTL_A;
+ DP_AUX_CTL_A : constant Registers_Index := DDI_AUX_CTL_A;
+ DP_AUX_DATA_A_1 : constant Registers_Index := DDI_AUX_DATA_A_1;
+ DP_AUX_DATA_A_2 : constant Registers_Index := DDI_AUX_DATA_A_2;
+ DP_AUX_DATA_A_3 : constant Registers_Index := DDI_AUX_DATA_A_3;
+ DP_AUX_DATA_A_4 : constant Registers_Index := DDI_AUX_DATA_A_4;
+ DP_AUX_DATA_A_5 : constant Registers_Index := DDI_AUX_DATA_A_5;
+
+ ---------------------------------------------------------------------------
+
+ Default_Timeout_MS : constant := 10;
+
+ ---------------------------------------------------------------------------
+
+ procedure Posting_Read
+ (Register : in Registers_Index)
+ with
+ Global => (In_Out => Register_State),
+ Depends => (Register_State =>+ (Register)),
+ Pre => True,
+ Post => True;
+
+ pragma Warnings (GNATprove, Off, "unused variable ""Verbose""",
+ Reason => "Only used on debugging path");
+ procedure Read
+ (Register : in Registers_Index;
+ Value : out Word32;
+ Verbose : in Boolean := True)
+ with
+ Global => (In_Out => Register_State),
+ Depends => ((Value, Register_State) => (Register, Register_State),
+ null => Verbose),
+ Pre => True,
+ Post => True;
+ pragma Warnings (GNATprove, On, "unused variable ""Verbose""");
+
+ procedure Write
+ (Register : Registers_Index;
+ Value : Word32)
+ with
+ Global => (In_Out => Register_State),
+ Depends => (Register_State => (Register, Register_State, Value)),
+ Pre => True,
+ Post => True;
+
+ procedure Is_Set_Mask
+ (Register : in Registers_Index;
+ Mask : in Word32;
+ Result : out Boolean);
+
+ pragma Warnings (GNATprove, Off, "unused initial value of ""Verbose""",
+ Reason => "Only used on debugging path");
+ procedure Wait_Set_Mask
+ (Register : Registers_Index;
+ Mask : Word32;
+ TOut_MS : Natural := Default_Timeout_MS;
+ Verbose : Boolean := False);
+
+ procedure Wait_Unset_Mask
+ (Register : Registers_Index;
+ Mask : Word32;
+ TOut_MS : Natural := Default_Timeout_MS;
+ Verbose : Boolean := False);
+ pragma Warnings (GNATprove, On, "unused initial value of ""Verbose""");
+
+ procedure Set_Mask
+ (Register : Registers_Index;
+ Mask : Word32);
+
+ procedure Unset_Mask
+ (Register : Registers_Index;
+ Mask : Word32);
+
+ procedure Unset_And_Set_Mask
+ (Register : Registers_Index;
+ Mask_Unset : Word32;
+ Mask_Set : Word32);
+
+ pragma Warnings (Off, "declaration of ""Write_GTT"" hides one at *");
+ procedure Write_GTT
+ (GTT_Page : GTT_Range;
+ Device_Address : GTT_Address_Type;
+ Valid : Boolean)
+ with
+ Global => (In_Out => GTT_State),
+ Depends => (GTT_State =>+ (GTT_Page, Device_Address, Valid)),
+ Pre => True,
+ Post => True;
+ pragma Warnings (On, "declaration of ""Write_GTT"" hides one at *");
+
+ procedure Set_Register_Base (Base : Word64)
+ with
+ Global => (Output => Address_State),
+ Depends => (Address_State => Base),
+ Pre => True,
+ Post => True;
+
+end HW.GFX.GMA.Registers;
diff --git a/common/hw-gfx-gma.adb b/common/hw-gfx-gma.adb
new file mode 100644
index 0000000..d004381
--- /dev/null
+++ b/common/hw-gfx-gma.adb
@@ -0,0 +1,836 @@
+--
+-- Copyright (C) 2014-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.EDID;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Power_And_Clocks;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.PLLs;
+with HW.GFX.GMA.Port_Detect;
+with HW.GFX.GMA.Connectors;
+with HW.GFX.GMA.Connector_Info;
+with HW.GFX.GMA.Pipe_Setup;
+
+with System;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+use type HW.Word8;
+use type HW.Int32;
+
+package body HW.GFX.GMA
+ with Refined_State =>
+ (State =>
+ (Registers.Address_State,
+ PLLs.State, Panel.Panel_State,
+ Cur_Configs, Allocated_PLLs, DP_Links,
+ HPD_Delay, Wait_For_HPD),
+ Init_State => Initialized,
+ Config_State => Config.Valid_Port_GPU,
+ Device_State =>
+ (Registers.Register_State, Registers.GTT_State))
+is
+
+ subtype Port_Name is String (1 .. 8);
+ type Port_Name_Array is array (Port_Type) of Port_Name;
+ Port_Names : constant Port_Name_Array :=
+ (Disabled => "Disabled",
+ Internal => "Internal",
+ DP1 => "DP1 ",
+ DP2 => "DP2 ",
+ DP3 => "DP3 ",
+ Digital1 => "Digital1",
+ Digital2 => "Digital2",
+ Digital3 => "Digital3",
+ Analog => "Analog ");
+
+ package Display_Controller renames Pipe_Setup;
+
+ type PLLs_Type is array (Config_Index) of PLLs.T;
+
+ type Links_Type is array (Config_Index) of DP_Link;
+
+ type HPD_Type is array (Port_Type) of Boolean;
+ type HPD_Delay_Type is array (Port_Type) of Time.T;
+
+ Cur_Configs : Configs_Type;
+ Allocated_PLLs : PLLs_Type;
+ DP_Links : Links_Type;
+ HPD_Delay : HPD_Delay_Type;
+ Wait_For_HPD : HPD_Type;
+ Initialized : Boolean := False;
+
+ subtype Active_Port_Type is Port_Type range Port_Type'Succ (Disabled) .. Port_Type'Last;
+
+ ----------------------------------------------------------------------------
+
+ function To_GPU_Port
+ (Configs : Configs_Type;
+ Idx : Config_Index)
+ return GPU_Port
+ is
+ begin
+ return
+ (case Config.CPU is
+ when Ironlake .. Ivybridge => -- everything but eDP through FDI/PCH
+ (if Config.Internal_Is_EDP and then Configs (Idx).Port = Internal
+ then
+ DIGI_A
+ else
+ (case Idx is
+ -- FDIs are fixed to the CPU pipe
+ when Primary => DIGI_B,
+ when Secondary => DIGI_C,
+ when Tertiary => DIGI_D)),
+ when Haswell .. Skylake => -- everything but VGA directly on CPU
+ (case Configs (Idx).Port is
+ when Disabled => GPU_Port'First,
+ when Internal => DIGI_A, -- LVDS not available
+ when Digital1 | DP1 => DIGI_B,
+ when Digital2 | DP2 => DIGI_C,
+ when Digital3 | DP3 => DIGI_D,
+ when Analog => DIGI_E));
+ end To_GPU_Port;
+
+ function To_PCH_Port (Port : Active_Port_Type) return PCH_Port
+ is
+ begin
+ return
+ (case Port is
+ when Internal => PCH_LVDS, -- will be ignored if Internal is DP
+ when Analog => PCH_DAC,
+ when Digital1 => PCH_HDMI_B,
+ when Digital2 => PCH_HDMI_C,
+ when Digital3 => PCH_HDMI_D,
+ when DP1 => PCH_DP_B,
+ when DP2 => PCH_DP_C,
+ when DP3 => PCH_DP_D);
+ end To_PCH_Port;
+
+ function To_Display_Type (Port : Active_Port_Type) return Display_Type
+ with Pre => True
+ is
+ begin
+ return
+ (case Port is
+ when Internal => Config.Internal_Display,
+ when Analog => VGA,
+ when Digital1 |
+ Digital2 |
+ Digital3 => HDMI,
+ when DP1 |
+ DP2 |
+ DP3 => DP);
+ end To_Display_Type;
+
+ procedure Configure_FDI_Link
+ (Port_Cfg : in out Port_Config;
+ Success : out Boolean)
+ with Pre => True
+ is
+ procedure Limit_Lane_Count
+ is
+ FDI_TX_CTL_FDI_TX_ENABLE : constant := 1 * 2 ** 31;
+ Enabled : Boolean;
+ begin
+ -- if DIGI_D enabled: (FDI names are off by one)
+ Registers.Is_Set_Mask
+ (Register => Registers.FDI_TX_CTL_C,
+ Mask => FDI_TX_CTL_FDI_TX_ENABLE,
+ Result => Enabled);
+ if Enabled then
+ Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_2;
+ end if;
+ end Limit_Lane_Count;
+ begin
+ Port_Cfg.FDI.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_2_7;
+ Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count :=
+ Config.FDI_Lane_Count (Port_Cfg.Port);
+ Port_Cfg.FDI.Receiver_Caps.Enhanced_Framing := True;
+ if Config.Has_FDI_C and then Port_Cfg.Port = DIGI_C then
+ Limit_Lane_Count;
+ end if;
+ DP_Info.Preferred_Link_Setting (Port_Cfg.FDI, Port_Cfg.Mode, Success);
+ end Configure_FDI_Link;
+
+ procedure Fill_Port_Config
+ (Port_Cfg : out Port_Config;
+ Configs : in Configs_Type;
+ Idx : in Config_Index;
+ Success : out Boolean)
+ with Pre => True
+ is
+ begin
+ Success :=
+ Config.Supported_Pipe (Idx) and then
+ Config.Valid_Port (Configs (Idx).Port) and then
+ Configs (Idx).Port /= Disabled;
+
+ if Success then
+ declare
+ Port : constant Port_Type := Configs (Idx).Port;
+ Mode : constant Mode_Type := Configs (Idx).Mode;
+ Link : constant DP_Link := DP_Links (Idx);
+ begin
+ Port_Cfg := Port_Config'
+ (Port => To_GPU_Port (Configs, Idx),
+ PCH_Port => To_PCH_Port (Port),
+ Display => To_Display_Type (Port),
+ Mode => Mode,
+ Is_FDI => Config.FDI_Port (To_GPU_Port (Configs, Idx)),
+ FDI => Default_DP,
+ DP => Link);
+ if Port_Cfg.Mode.BPC = Auto_BPC then
+ Port_Cfg.Mode.BPC := Connector_Info.Default_BPC (Port_Cfg);
+ end if;
+ end;
+ else
+ Port_Cfg := Port_Config'
+ (Port => GPU_Port'First,
+ PCH_Port => PCH_Port'First,
+ Display => Display_Type'First,
+ Mode => Invalid_Mode,
+ Is_FDI => False,
+ FDI => Default_DP,
+ DP => Default_DP);
+ end if;
+ end Fill_Port_Config;
+
+ ----------------------------------------------------------------------------
+
+ function To_Controller
+ (Dsp_Config : Config_Index) return Display_Controller.Controller_Type
+ is
+ Result : Display_Controller.Controller_Type;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ case Dsp_Config is
+ when Primary =>
+ Result := Display_Controller.Controllers (Display_Controller.A);
+ when Secondary =>
+ Result := Display_Controller.Controllers (Display_Controller.B);
+ when Tertiary =>
+ Result := Display_Controller.Controllers (Display_Controller.C);
+ end case;
+ return Result;
+ end To_Controller;
+
+ ----------------------------------------------------------------------------
+
+ function To_Head
+ (N_Config : Config_Index;
+ Port : Active_Port_Type)
+ return Display_Controller.Head_Type
+ is
+ Result : Display_Controller.Head_Type;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Config.Has_EDP_Pipe and then Port = Internal then
+ Result := Display_Controller.Heads (Display_Controller.Head_EDP);
+ else
+ case N_Config is
+ when Primary =>
+ Result := Display_Controller.Heads (Display_Controller.Head_A);
+ when Secondary =>
+ Result := Display_Controller.Heads (Display_Controller.Head_B);
+ when Tertiary =>
+ Result := Display_Controller.Heads (Display_Controller.Head_C);
+ end case;
+ end if;
+ return Result;
+ end To_Head;
+
+ ----------------------------------------------------------------------------
+
+ procedure Legacy_VGA_Off
+ is
+ Reg8 : Word8;
+ begin
+ -- disable legacy VGA plane, taking over control now
+ Port_IO.OutB (VGA_SR_INDEX, VGA_SR01);
+ Port_IO.InB (Reg8, VGA_SR_DATA);
+ Port_IO.OutB (VGA_SR_DATA, Reg8 or 1 * 2 ** 5);
+ Time.U_Delay (100); -- PRM says 100us, Linux does 300
+ Registers.Set_Mask (Registers.VGACNTRL, 1 * 2 ** 31);
+ end Legacy_VGA_Off;
+
+ ----------------------------------------------------------------------------
+
+ function Port_Configured
+ (Configs : Configs_Type;
+ Port : Port_Type)
+ return Boolean
+ with
+ Global => null
+ is
+ begin
+ return Configs (Primary).Port = Port or
+ Configs (Secondary).Port = Port or
+ Configs (Tertiary).Port = Port;
+ end Port_Configured;
+
+ procedure Scan_Ports
+ (Configs : out Configs_Type;
+ Ports : in Port_List)
+ is
+ Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
+ Port_Idx : Port_List_Range := Port_List_Range'First;
+ Port_Cfg : Port_Config;
+ Success : Boolean := False;
+ begin
+ Configs := (Config_Index =>
+ (Port => Disabled,
+ Mode => Invalid_Mode,
+ Framebuffer => Default_FB));
+
+ for Config_Idx in Config_Index loop
+ while Ports (Port_Idx) /= Disabled loop
+ if not Port_Configured (Configs, Ports (Port_Idx)) then
+ Configs (Config_Idx).Port := Ports (Port_Idx);
+ Fill_Port_Config (Port_Cfg, Configs, Config_Idx, Success);
+
+ if Success then
+ -- May need power to probe port
+ if Port_Cfg.Display = DP then
+ Power_And_Clocks.Power_Up (Cur_Configs, Configs);
+ end if;
+ if Ports (Port_Idx) = Internal then
+ Panel.On;
+ end if;
+
+ Connector_Info.Read_EDID (Raw_EDID, Port_Cfg, Success);
+ end if;
+
+ if Success and then EDID.Has_Preferred_Mode (Raw_EDID) then
+ Configs (Config_Idx).Mode := EDID.Preferred_Mode (Raw_EDID);
+ else
+ Configs (Config_Idx).Port := Disabled;
+
+ if Ports (Port_Idx) = Internal and
+ not Port_Configured (Cur_Configs, Internal)
+ then
+ Panel.Off;
+ end if;
+ end if;
+ end if;
+
+ exit when Port_Idx = Port_List_Range'Last;
+ Port_Idx := Port_List_Range'Succ (Port_Idx);
+
+ exit when Success;
+ end loop;
+ end loop;
+
+ Power_And_Clocks.Power_Set_To (Cur_Configs);
+ end Scan_Ports;
+
+ procedure Auto_Configure
+ (Configs : in out Configs_Type;
+ Keep_Power : in Boolean := False)
+ is
+ Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
+ Success : Boolean;
+
+ Config_Idx : Config_Index;
+ Port_Cfg : Port_Config;
+
+ function Free_Config return Boolean
+ with
+ Pre => True
+ is
+ begin
+ return Port_Configured (Configs, Disabled);
+ end Free_Config;
+
+ function First_Free_Config return Config_Index
+ with
+ Pre => Free_Config
+ is
+ begin
+ return (if Configs (Primary).Port = Disabled then Primary else
+ (if Configs (Secondary).Port = Disabled then Secondary
+ else Tertiary));
+ end First_Free_Config;
+ begin
+ -- TODO: Only check ports with hot-plug event?
+
+ if Config.Has_Internal_Display and then
+ not Keep_Power and then
+ not Port_Configured (Cur_Configs, Internal)
+ then
+ Panel.On (Wait => False);
+ end if;
+
+ -- Check if displays are still connected
+ for I in Config_Index loop
+ if Configs (I).Port /= Disabled then
+ Fill_Port_Config (Port_Cfg, Configs, I, Success);
+ if Success then
+ Connector_Info.Read_EDID
+ (Raw_EDID => Raw_EDID,
+ Port_Cfg => Port_Cfg,
+ Success => Success);
+ end if;
+ if not Success or else
+ not EDID.Has_Preferred_Mode (Raw_EDID) or else
+ Configs (I).Mode /= EDID.Preferred_Mode (Raw_EDID)
+ then
+ Configs (I).Port := Disabled;
+ end if;
+ end if;
+ end loop;
+
+ -- Add new displays as long as there is a free pipe config
+ for Port in Active_Port_Type loop
+ if Free_Config and then not Port_Configured (Configs, Port) then
+ Config_Idx := First_Free_Config;
+ Configs (Config_Idx).Port := Port;
+ Fill_Port_Config (Port_Cfg, Configs, Config_Idx, Success);
+
+ if Success then
+ -- Need power to probe port
+ if not Keep_Power and then To_Display_Type (Port) = DP then
+ Power_And_Clocks.Power_Up (Cur_Configs, Configs);
+ end if;
+ if not Keep_Power and then Port = Internal then
+ Panel.Wait_On;
+ end if;
+
+ Connector_Info.Read_EDID
+ (Raw_EDID => Raw_EDID,
+ Port_Cfg => Port_Cfg,
+ Success => Success);
+ end if;
+
+ if Success and then EDID.Has_Preferred_Mode (Raw_EDID) then
+ Configs (Config_Idx) := Config_Type'
+ (Port => Port,
+ Framebuffer => Configs (Config_Idx).Framebuffer,
+ Mode => EDID.Preferred_Mode (Raw_EDID));
+ else
+ Configs (Config_Idx).Port := Disabled;
+ end if;
+ end if;
+ end loop;
+
+ if not Keep_Power then
+ Power_And_Clocks.Power_Set_To (Cur_Configs);
+
+ if Config.Has_Internal_Display and then
+ not Port_Configured (Cur_Configs, Internal)
+ then
+ Panel.Off;
+ end if;
+ end if;
+ end Auto_Configure;
+
+ ----------------------------------------------------------------------------
+
+ procedure Update_Outputs (Configs : Configs_Type)
+ is
+ Did_Power_Up : Boolean := False;
+
+ HPD, HPD_Delay_Over, Success : Boolean;
+ Old_Config, New_Config : Config_Type;
+ Old_Configs : Configs_Type;
+ Port_Cfg : Port_Config;
+
+ procedure Check_HPD
+ (Port_Cfg : in Port_Config;
+ Port : in Port_Type;
+ Detected : out Boolean)
+ is
+ begin
+ HPD_Delay_Over := Time.Timed_Out (HPD_Delay (Port));
+ if HPD_Delay_Over then
+ Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
+ HPD_Delay (Port) := Time.MS_From_Now (333);
+ else
+ Detected := False;
+ end if;
+ end Check_HPD;
+ begin
+ Old_Configs := Cur_Configs;
+
+ for I in Config_Index loop
+ HPD := False;
+
+ Old_Config := Cur_Configs (I);
+ New_Config := Configs (I);
+
+ Fill_Port_Config (Port_Cfg, Old_Configs, I, Success);
+ if Success then
+ Check_HPD (Port_Cfg, Old_Config.Port, HPD);
+ end if;
+
+ -- Connector changed?
+ if (Success and then HPD) or
+ Old_Config.Port /= New_Config.Port or
+ Old_Config.Mode /= New_Config.Mode
+ then
+ if Old_Config.Port /= Disabled then
+ if Success then
+ pragma Debug (Debug.New_Line);
+ pragma Debug (Debug.Put_Line
+ ("Disabling port " & Port_Names (Old_Config.Port)));
+
+ Connectors.Pre_Off (Port_Cfg);
+
+ Display_Controller.Off
+ (To_Controller (I), To_Head (I, Old_Config.Port));
+
+ Connectors.Post_Off (Port_Cfg);
+ end if;
+
+ -- Free PLL
+ PLLs.Free (Allocated_PLLs (I));
+
+ Cur_Configs (I).Port := Disabled;
+ end if;
+
+ if New_Config.Port /= Disabled then
+ Fill_Port_Config (Port_Cfg, Configs, I, Success);
+
+ if Success and then Wait_For_HPD (New_Config.Port) then
+ Check_HPD (Port_Cfg, New_Config.Port, Success);
+ Wait_For_HPD (New_Config.Port) := not Success;
+ end if;
+
+ if Success then
+ pragma Debug (Debug.New_Line);
+ pragma Debug (Debug.Put_Line
+ ("Trying to enable port " & Port_Names (New_Config.Port)));
+
+ if not Did_Power_Up then
+ Power_And_Clocks.Power_Up (Old_Configs, Configs);
+ Did_Power_Up := True;
+ end if;
+
+ if Port_Cfg.Is_FDI then
+ Configure_FDI_Link (Port_Cfg, Success);
+ end if;
+ end if;
+
+ if Success then
+ Connector_Info.Preferred_Link_Setting
+ (Port_Cfg => Port_Cfg,
+ Success => Success);
+ end if;
+
+ while Success loop
+ pragma Loop_Invariant (New_Config.Port in Active_Port_Type);
+
+ PLLs.Alloc
+ (Port_Cfg => Port_Cfg,
+ PLL => Allocated_PLLs (I),
+ Success => Success);
+
+ if Success then
+ for Try in 1 .. 2 loop
+ pragma Loop_Invariant
+ (New_Config.Port in Active_Port_Type);
+
+ Connectors.Pre_On
+ (Port_Cfg => Port_Cfg,
+ PLL_Hint => PLLs.Register_Value
+ (Allocated_PLLs (I)),
+ Pipe_Hint => Display_Controller.Get_Pipe_Hint
+ (To_Head (I, New_Config.Port)),
+ Success => Success);
+
+ if Success then
+ Display_Controller.On
+ (Controller => To_Controller (I),
+ Head => To_Head (I, New_Config.Port),
+ Port_Cfg => Port_Cfg,
+ Framebuffer => New_Config.Framebuffer);
+
+ Connectors.Post_On
+ (Port_Cfg => Port_Cfg,
+ PLL_Hint => PLLs.Register_Value
+ (Allocated_PLLs (I)),
+ Success => Success);
+
+ if not Success then
+ Display_Controller.Off
+ (To_Controller (I),
+ To_Head (I, New_Config.Port));
+ Connectors.Post_Off (Port_Cfg);
+ end if;
+ end if;
+
+ exit when Success;
+ end loop;
+ exit when Success; -- connection established => stop loop
+
+ -- connection failed
+ PLLs.Free (Allocated_PLLs (I));
+ end if;
+
+ Connector_Info.Next_Link_Setting
+ (Port_Cfg => Port_Cfg,
+ Success => Success);
+ end loop;
+
+ if Success then
+ pragma Debug (Debug.Put_Line
+ ("Enabled port " & Port_Names (New_Config.Port)));
+ Cur_Configs (I) := New_Config;
+ DP_Links (I) := Port_Cfg.DP;
+ else
+ Wait_For_HPD (New_Config.Port) := True;
+ if New_Config.Port = Internal then
+ Panel.Off;
+ end if;
+ end if;
+ else
+ Cur_Configs (I) := New_Config;
+ end if;
+ elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
+ Old_Config.Port /= Disabled
+ then
+ Display_Controller.Update_Offset
+ (Controller => To_Controller (I),
+ Framebuffer => New_Config.Framebuffer);
+ Cur_Configs (I) := New_Config;
+ end if;
+ end loop;
+
+ if Did_Power_Up then
+ Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
+ end if;
+
+ end Update_Outputs;
+
+ ----------------------------------------------------------------------------
+
+ procedure Initialize
+ (MMIO_Base : in Word64 := 0;
+ Write_Delay : in Word64 := 0;
+ Success : out Boolean)
+ with
+ Refined_Global =>
+ (In_Out =>
+ (Config.Valid_Port_GPU,
+ Registers.Register_State, Port_IO.State),
+ Input =>
+ (Time.State),
+ Output =>
+ (Registers.Address_State,
+ PLLs.State, Panel.Panel_State,
+ Cur_Configs, Allocated_PLLs, DP_Links,
+ HPD_Delay, Wait_For_HPD, Initialized))
+ is
+ use type HW.Word64;
+
+ Now : constant Time.T := Time.Now;
+
+ procedure Check_Platform (Success : out Boolean)
+ is
+ Audio_VID_DID : Word32;
+ begin
+ case Config.CPU is
+ when Haswell .. Skylake =>
+ Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
+ when Ironlake .. Ivybridge =>
+ Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
+ end case;
+ Success :=
+ (case Config.CPU is
+ when Skylake => Audio_VID_DID = 16#8086_2809#,
+ when Broadwell => Audio_VID_DID = 16#8086_2808#,
+ when Haswell => Audio_VID_DID = 16#8086_2807#,
+ when Ivybridge |
+ Sandybridge => Audio_VID_DID = 16#8086_2806# or
+ Audio_VID_DID = 16#8086_2805#,
+ when Ironlake => Audio_VID_DID = 16#8086_2804#); -- not sure
+ end Check_Platform;
+ begin
+ pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
+ Reason => "Write_Delay is used for debugging only");
+
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
+
+ Wait_For_HPD := HPD_Type'(others => False);
+ HPD_Delay := HPD_Delay_Type'(others => Now);
+ DP_Links := Links_Type'(others => HW.GFX.Default_DP);
+ Allocated_PLLs := (others => PLLs.Invalid);
+ Cur_Configs := Configs_Type'
+ (others => Config_Type'
+ (Port => Disabled,
+ Framebuffer => HW.GFX.Default_FB,
+ Mode => HW.GFX.Invalid_Mode));
+ Registers.Set_Register_Base
+ (if MMIO_Base /= 0 then
+ MMIO_Base
+ else
+ Config.Default_MMIO_Base);
+ PLLs.Initialize;
+
+ Check_Platform (Success);
+ if not Success then
+ pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
+
+ Panel.Static_Init; -- for flow analysis
+
+ Initialized := False;
+ return;
+ end if;
+
+ Panel.Setup_PP_Sequencer;
+ Port_Detect.Initialize;
+
+ Power_And_Clocks.Pre_All_Off;
+
+ Legacy_VGA_Off;
+
+ Connectors.Pre_All_Off;
+ Display_Controller.All_Off;
+ Connectors.Post_All_Off;
+ PLLs.All_Off;
+
+ Power_And_Clocks.Post_All_Off;
+
+ -------------------- Now restart from a clean state ---------------------
+ Power_And_Clocks.Initialize;
+
+ Initialized := True;
+
+ end Initialize;
+
+ function Is_Initialized return Boolean
+ with
+ Refined_Post => Is_Initialized'Result = Initialized
+ is
+ begin
+ return Initialized;
+ end Is_Initialized;
+
+ ----------------------------------------------------------------------------
+
+ procedure Write_GTT
+ (GTT_Page : GTT_Range;
+ Device_Address : GTT_Address_Type;
+ Valid : Boolean) is
+ begin
+ Registers.Write_GTT (GTT_Page, Device_Address, Valid);
+ end Write_GTT;
+
+ procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
+ is
+ FB_Size : constant Pos32 :=
+ FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
+ Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
+ begin
+ for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
+ loop
+ Registers.Write_GTT
+ (GTT_Page => Idx,
+ Device_Address => Phys_Addr,
+ Valid => True);
+ Phys_Addr := Phys_Addr + 4096;
+ end loop;
+ end Setup_Default_GTT;
+
+ ----------------------------------------------------------------------------
+
+ procedure Dump_Configs (Configs : Configs_Type)
+ is
+ subtype Pipe_Name is String (1 .. 9);
+ type Pipe_Name_Array is array (Config_Index) of Pipe_Name;
+ Pipe_Names : constant Pipe_Name_Array :=
+ (Primary => "Primary ",
+ Secondary => "Secondary",
+ Tertiary => "Tertiary ");
+ begin
+ Debug.New_Line;
+ Debug.Put_Line ("CONFIG => ");
+ for Pipe in Config_Index loop
+ if Pipe = Config_Index'First then
+ Debug.Put (" (");
+ else
+ Debug.Put (" ");
+ end if;
+ Debug.Put_Line (Pipe_Names (Pipe) & " =>");
+ Debug.Put_Line
+ (" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
+ Debug.Put_Line (" Framebuffer =>");
+ Debug.Put (" (Width => ");
+ Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
+ Debug.Put_Line (",");
+ Debug.Put (" Height => ");
+ Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
+ Debug.Put_Line (",");
+ Debug.Put (" Stride => ");
+ Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
+ Debug.Put_Line (",");
+ Debug.Put (" Offset => ");
+ Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
+ Debug.Put_Line (",");
+ Debug.Put (" BPC => ");
+ Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
+ Debug.Put_Line ("),");
+ Debug.Put_Line (" Mode =>");
+ Debug.Put (" (Dotclock => ");
+ Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
+ Debug.Put_Line (",");
+ Debug.Put (" H_Visible => ");
+ Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
+ Debug.Put_Line (",");
+ Debug.Put (" H_Sync_Begin => ");
+ Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
+ Debug.Put_Line (",");
+ Debug.Put (" H_Sync_End => ");
+ Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
+ Debug.Put_Line (",");
+ Debug.Put (" H_Total => ");
+ Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
+ Debug.Put_Line (",");
+ Debug.Put (" V_Visible => ");
+ Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
+ Debug.Put_Line (",");
+ Debug.Put (" V_Sync_Begin => ");
+ Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
+ Debug.Put_Line (",");
+ Debug.Put (" V_Sync_End => ");
+ Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
+ Debug.Put_Line (",");
+ Debug.Put (" V_Total => ");
+ Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
+ Debug.Put_Line (",");
+ Debug.Put_Line (" H_Sync_Active_High => " &
+ (if Configs (Pipe).Mode.H_Sync_Active_High
+ then "True,"
+ else "False,"));
+ Debug.Put_Line (" V_Sync_Active_High => " &
+ (if Configs (Pipe).Mode.V_Sync_Active_High
+ then "True,"
+ else "False,"));
+ Debug.Put (" BPC => ");
+ Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
+ if Pipe /= Config_Index'Last then
+ Debug.Put_Line (")),");
+ else
+ Debug.Put_Line (")));");
+ end if;
+ end loop;
+ end Dump_Configs;
+
+end HW.GFX.GMA;
diff --git a/common/hw-gfx-gma.ads b/common/hw-gfx-gma.ads
new file mode 100644
index 0000000..465cf08
--- /dev/null
+++ b/common/hw-gfx-gma.ads
@@ -0,0 +1,138 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.Port_IO;
+
+package HW.GFX.GMA
+with
+ Abstract_State =>
+ (State,
+ Init_State,
+ Config_State,
+ (Device_State with External)),
+ Initializes =>
+ (Init_State,
+ Config_State)
+is
+
+ type CPU_Type is
+ (Ironlake,
+ Sandybridge,
+ Ivybridge,
+ Haswell,
+ Broadwell,
+ Skylake);
+
+ type CPU_Variant is (Normal, ULT);
+
+ type Port_Type is
+ (Disabled,
+ Internal,
+ DP1,
+ DP2,
+ DP3,
+ Digital1,
+ Digital2,
+ Digital3,
+ Analog);
+ type Port_List_Range is range 0 .. 7;
+ type Port_List is array (Port_List_Range) of Port_Type;
+
+ type Config_Type is record
+ Port : Port_Type;
+ Framebuffer : Framebuffer_Type;
+ Mode : Mode_Type;
+ end record;
+ type Config_Index is (Primary, Secondary, Tertiary);
+ type Configs_Type is array (Config_Index) of Config_Type;
+
+ procedure Initialize
+ (MMIO_Base : in Word64 := 0;
+ Write_Delay : in Word64 := 0;
+ Success : out Boolean)
+ with
+ Global =>
+ (In_Out => (Config_State, Device_State, Port_IO.State),
+ Output => (State, Init_State),
+ Input => (Time.State)),
+ Post => Success = Is_Initialized;
+ function Is_Initialized return Boolean
+ with
+ Global => (Input => Init_State);
+
+ procedure Legacy_VGA_Off;
+
+ procedure Scan_Ports
+ (Configs : out Configs_Type;
+ Ports : in Port_List);
+ procedure Auto_Configure
+ (Configs : in out Configs_Type;
+ Keep_Power : in Boolean := False);
+ procedure Update_Outputs (Configs : Configs_Type);
+
+ pragma Warnings (GNATprove, Off, "subprogram ""Dump_Configs"" has no effect",
+ Reason => "It's only used for debugging");
+ procedure Dump_Configs (Configs : Configs_Type);
+
+ type GTT_Address_Type is mod 2 ** 39;
+ type GTT_Range is range 0 .. 16#8_0000# - 1;
+ procedure Write_GTT
+ (GTT_Page : GTT_Range;
+ Device_Address : GTT_Address_Type;
+ Valid : Boolean);
+
+ procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32);
+
+private
+
+ type GPU_Port is (DIGI_A, DIGI_B, DIGI_C, DIGI_D, DIGI_E);
+
+ subtype Digital_Port is GPU_Port range DIGI_A .. DIGI_E;
+
+ type PCH_Port is
+ (PCH_DAC, PCH_LVDS,
+ PCH_HDMI_B, PCH_HDMI_C, PCH_HDMI_D,
+ PCH_DP_B, PCH_DP_C, PCH_DP_D);
+
+ subtype PCH_HDMI_Port is PCH_Port range PCH_HDMI_B .. PCH_HDMI_D;
+ subtype PCH_DP_Port is PCH_Port range PCH_DP_B .. PCH_DP_D;
+
+ type Display_Type is (None, LVDS, DP, HDMI, VGA);
+
+ subtype Internal_Type is Display_Type range None .. DP;
+
+ type Port_Config is
+ record
+ Port : GPU_Port;
+ PCH_Port : GMA.PCH_Port;
+ Display : Display_Type;
+ Mode : Mode_Type;
+ Is_FDI : Boolean;
+ FDI : DP_Link;
+ DP : DP_Link;
+ end record;
+
+ type FDI_Training_Type is (Simple_Training, Full_Training, Auto_Training);
+
+ ----------------------------------------------------------------------------
+
+ type DP_Port is (DP_A, DP_B, DP_C, DP_D);
+
+ ----------------------------------------------------------------------------
+
+ VGA_SR_INDEX : constant Port_IO.Port_Type := 16#03c4#;
+ VGA_SR_DATA : constant Port_IO.Port_Type := 16#03c5#;
+ VGA_SR01 : constant Word8 := 16#01#;
+
+end HW.GFX.GMA;
diff --git a/common/hw-gfx-i2c.ads b/common/hw-gfx-i2c.ads
new file mode 100644
index 0000000..db5e5cc
--- /dev/null
+++ b/common/hw-gfx-i2c.ads
@@ -0,0 +1,21 @@
+--
+-- Copyright (C) 2015 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.I2C is
+
+ type Transfer_Address is mod 2 ** 7;
+ subtype Transfer_Length is Natural range 0 .. 128;
+ subtype Transfer_Index is Natural range 0 .. Transfer_Length'Last - 1;
+ subtype Transfer_Data is Buffer (Transfer_Index);
+
+end HW.GFX.I2C;
diff --git a/common/hw-gfx.ads b/common/hw-gfx.ads
new file mode 100644
index 0000000..da0d6fb
--- /dev/null
+++ b/common/hw-gfx.ads
@@ -0,0 +1,155 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW;
+
+use type HW.Pos64;
+use type HW.Word32;
+
+package HW.GFX is
+
+ -- implementation only supports 4800p for now ;-)
+ subtype Width_Type is Pos32 range 1 .. 4800;
+ subtype Height_Type is Pos32 range 1 .. 7680;
+
+ Auto_BPC : constant := 5;
+ subtype BPC_Type is Int64 range Auto_BPC .. 16;
+
+ type Framebuffer_Type is
+ record
+ Width : Width_Type;
+ Height : Height_Type;
+ BPC : BPC_Type;
+ Stride : Width_Type;
+ Offset : Word32;
+ end record;
+
+ Default_FB : constant Framebuffer_Type := Framebuffer_Type'
+ (Width => 1,
+ Height => 1,
+ BPC => 8,
+ Stride => 1,
+ Offset => 0);
+
+ subtype Frequency_Type is Pos64 range 25_000_000 .. 600_000_000;
+
+ type DP_Lane_Count is (DP_Lane_Count_1, DP_Lane_Count_2, DP_Lane_Count_4);
+ subtype DP_Lane_Count_Type is Pos64 range 1 .. 4;
+ type DP_Lane_Count_Integers is array (DP_Lane_Count) of DP_Lane_Count_Type;
+ Lane_Count_As_Integer : constant DP_Lane_Count_Integers :=
+ DP_Lane_Count_Integers'
+ (DP_Lane_Count_1 => 1, DP_Lane_Count_2 => 2, DP_Lane_Count_4 => 4);
+
+ type DP_Bandwidth is (DP_Bandwidth_1_62, DP_Bandwidth_2_7, DP_Bandwidth_5_4);
+ for DP_Bandwidth use
+ (DP_Bandwidth_1_62 => 6, DP_Bandwidth_2_7 => 10, DP_Bandwidth_5_4 => 20);
+ for DP_Bandwidth'Size use 8;
+ subtype DP_Symbol_Rate_Type is Pos64 range 1 .. 810_000_000;
+ type DP_Symbol_Rate_Array is array (DP_Bandwidth) of DP_Symbol_Rate_Type;
+ DP_Symbol_Rate : constant DP_Symbol_Rate_Array := DP_Symbol_Rate_Array'
+ (DP_Bandwidth_1_62 => 162_000_000,
+ DP_Bandwidth_2_7 => 270_000_000,
+ DP_Bandwidth_5_4 => 540_000_000);
+
+ type DP_Caps is record
+ Rev : Word8;
+ Max_Link_Rate : DP_Bandwidth;
+ Max_Lane_Count : DP_Lane_Count;
+ TPS3_Supported : Boolean;
+ Enhanced_Framing : Boolean;
+ No_Aux_Handshake : Boolean;
+ Aux_RD_Interval : Word8;
+ end record;
+
+ type DP_Link is
+ record
+ Receiver_Caps : DP_Caps;
+ Lane_Count : DP_Lane_Count;
+ Bandwidth : DP_Bandwidth;
+ Enhanced_Framing : Boolean;
+ Opportunistic_Training : Boolean;
+ end record;
+ Default_DP : constant DP_Link := DP_Link'
+ (Receiver_Caps => DP_Caps'
+ (Rev => 16#00#,
+ Max_Link_Rate => DP_Bandwidth'First,
+ Max_Lane_Count => DP_Lane_Count'First,
+ TPS3_Supported => False,
+ Enhanced_Framing => False,
+ No_Aux_Handshake => False,
+ Aux_RD_Interval => 16#00#),
+ Lane_Count => DP_Lane_Count'First,
+ Bandwidth => DP_Bandwidth'First,
+ Enhanced_Framing => False,
+ Opportunistic_Training => False);
+
+ type Mode_Type is
+ record
+ Dotclock : Frequency_Type;
+ H_Visible : Pos16;
+ H_Sync_Begin : Pos16;
+ H_Sync_End : Pos16;
+ H_Total : Pos16;
+ V_Visible : Pos16;
+ V_Sync_Begin : Pos16;
+ V_Sync_End : Pos16;
+ V_Total : Pos16;
+ H_Sync_Active_High : Boolean;
+ V_Sync_Active_High : Boolean;
+ BPC : BPC_Type;
+ end record;
+
+ ----------------------------------------------------------------------------
+ -- Constants
+ ----------------------------------------------------------------------------
+
+ -- modeline constants
+ -- Dotclock is calculated using: Refresh_Rate * H_Total * V_Total
+
+ M2560x1600_60 : constant Mode_Type := Mode_Type'
+ (60*(2720*1646), 2560, 2608, 2640, 2720, 1600, 1603, 1609, 1646, True, True, Auto_BPC);
+
+ M2560x1440_60 : constant Mode_Type := Mode_Type'
+ (60*(2720*1481), 2560, 2608, 2640, 2720, 1440, 1443, 1448, 1481, True, False, Auto_BPC);
+
+ M1920x1200_60 : constant Mode_Type := Mode_Type'
+ (60*(2080*1235), 1920, 1968, 2000, 2080, 1200, 1203, 1209, 1235, False, False, Auto_BPC);
+
+ M1920x1080_60 : constant Mode_Type := Mode_Type'
+ (60*(2185*1135), 1920, 2008, 2052, 2185, 1080, 1084, 1089, 1135, False, False, Auto_BPC);
+
+ M1680x1050_60 : constant Mode_Type := Mode_Type'
+ (60*(2256*1087), 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, False, True, Auto_BPC);
+
+ M1600x1200_60 : constant Mode_Type := Mode_Type'
+ (60*(2160*1250), 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, True, True, Auto_BPC);
+
+ M1600x900_60 : constant Mode_Type := Mode_Type'
+ (60*(2010*912), 1600, 1664, 1706, 2010, 900, 903, 906, 912, False, False, Auto_BPC);
+
+ M1440x900_60 : constant Mode_Type := Mode_Type'
+ (60*(1834*920), 1440, 1488, 1520, 1834, 900, 903, 909, 920, False, False, Auto_BPC);
+
+ M1366x768_60 : constant Mode_Type := Mode_Type'
+ (60*(1446*788), 1366, 1414, 1446, 1466, 768, 769, 773, 788, False, False, Auto_BPC);
+
+ M1280x1024_60 : constant Mode_Type := Mode_Type'
+ (60*(1712*1063), 1280, 1368, 1496, 1712, 1024, 1027, 1034, 1063, False, True, Auto_BPC);
+
+ M1024x768_60 : constant Mode_Type := Mode_Type'
+ (60*(1344*806), 1024, 1048, 1184, 1344, 768, 771, 777, 806, False, False, Auto_BPC);
+
+ Invalid_Mode : constant Mode_Type := Mode_Type'
+ (Frequency_Type'First, 1, 1, 1, 1, 1, 1, 1, 1, False, False, Auto_BPC);
+
+end HW.GFX;
diff --git a/common/ironlake/Makefile.inc b/common/ironlake/Makefile.inc
new file mode 100644
index 0000000..468bb95
--- /dev/null
+++ b/common/ironlake/Makefile.inc
@@ -0,0 +1,17 @@
+gfxinit-y += hw-gfx-gma-connectors-edp.adb
+gfxinit-y += hw-gfx-gma-connectors-edp.ads
+gfxinit-y += hw-gfx-gma-connectors-fdi.adb
+gfxinit-y += hw-gfx-gma-connectors-fdi.ads
+gfxinit-y += hw-gfx-gma-connectors.adb
+gfxinit-y += hw-gfx-gma-pch-dp.adb
+gfxinit-y += hw-gfx-gma-pch-dp.ads
+gfxinit-y += hw-gfx-gma-pch-hdmi.adb
+gfxinit-y += hw-gfx-gma-pch-hdmi.ads
+gfxinit-y += hw-gfx-gma-pch-lvds.adb
+gfxinit-y += hw-gfx-gma-pch-lvds.ads
+gfxinit-y += hw-gfx-gma-plls.adb
+gfxinit-y += hw-gfx-gma-plls.ads
+gfxinit-y += hw-gfx-gma-port_detect.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks_ironlake.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_ironlake.ads
diff --git a/common/ironlake/hw-gfx-gma-connectors-edp.adb b/common/ironlake/hw-gfx-gma-connectors-edp.adb
new file mode 100644
index 0000000..96930f3
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors-edp.adb
@@ -0,0 +1,297 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.DP_Training;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.EDP
+is
+
+ DP_CTL_DISPLAYPORT_ENABLE : constant := 1 * 2 ** 31;
+ DP_CTL_PIPE_SELECT_MASK : constant := 3 * 2 ** 29;
+ DP_CTL_PIPE_SELECT_SHIFT : constant := 29;
+ DP_CTL_VSWING_EMPH_SET_MASK : constant := 63 * 2 ** 22;
+ DP_CTL_PORT_WIDTH_MASK : constant := 7 * 2 ** 19;
+ DP_CTL_PORT_WIDTH_1_LANE : constant := 0 * 2 ** 19;
+ DP_CTL_PORT_WIDTH_2_LANES : constant := 1 * 2 ** 19;
+ DP_CTL_PORT_WIDTH_4_LANES : constant := 3 * 2 ** 19;
+ DP_CTL_ENHANCED_FRAMING_ENABLE : constant := 1 * 2 ** 18;
+ DP_CTL_PLL_FREQUENCY_MASK : constant := 3 * 2 ** 16;
+ DP_CTL_PLL_FREQUENCY_270 : constant := 0 * 2 ** 16;
+ DP_CTL_PLL_FREQUENCY_162 : constant := 1 * 2 ** 16;
+ DP_CTL_PORT_REVERSAL : constant := 1 * 2 ** 15;
+ DP_CTL_PLL_ENABLE : constant := 1 * 2 ** 14;
+ DP_CTL_LINK_TRAIN_MASK : constant := 3 * 2 ** 8;
+ DP_CTL_LINK_TRAIN_PAT1 : constant := 0 * 2 ** 8;
+ DP_CTL_LINK_TRAIN_PAT2 : constant := 1 * 2 ** 8;
+ DP_CTL_LINK_TRAIN_IDLE : constant := 2 * 2 ** 8;
+ DP_CTL_LINK_TRAIN_NORMAL : constant := 3 * 2 ** 8;
+ DP_CTL_ALT_SCRAMBLER_RESET : constant := 1 * 2 ** 6;
+ DP_CTL_VSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 4;
+ DP_CTL_HSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 3;
+ DP_CTL_PORT_DETECT : constant := 1 * 2 ** 2;
+
+ -- TODO? Values are for Ivy Bridge only
+ DP_CTL_VSWING_0_EMPH_0 : constant := 1 * 2 ** 27 + 1 * 2 ** 24 + 0 * 2 ** 22;
+ DP_CTL_VSWING_0_EMPH_1 : constant := 1 * 2 ** 27 + 2 * 2 ** 24 + 2 * 2 ** 22;
+ DP_CTL_VSWING_0_EMPH_2 : constant := 1 * 2 ** 27 + 3 * 2 ** 24 + 3 * 2 ** 22;
+ DP_CTL_VSWING_1_EMPH_0 : constant := 1 * 2 ** 27 + 4 * 2 ** 24 + 0 * 2 ** 22;
+ DP_CTL_VSWING_1_EMPH_1 : constant := 1 * 2 ** 27 + 5 * 2 ** 24 + 2 * 2 ** 22;
+ DP_CTL_VSWING_2_EMPH_0 : constant := 1 * 2 ** 27 + 6 * 2 ** 24 + 0 * 2 ** 22;
+ DP_CTL_VSWING_2_EMPH_1 : constant := 1 * 2 ** 27 + 7 * 2 ** 24 + 2 * 2 ** 22;
+
+ type DP_CTL_PORT_WIDTH_T is array (DP_Lane_Count) of Word32;
+ DP_CTL_PORT_WIDTH : constant DP_CTL_PORT_WIDTH_T :=
+ DP_CTL_PORT_WIDTH_T'
+ (DP_Lane_Count_1 => DP_CTL_PORT_WIDTH_1_LANE,
+ DP_Lane_Count_2 => DP_CTL_PORT_WIDTH_2_LANES,
+ DP_Lane_Count_4 => DP_CTL_PORT_WIDTH_4_LANES);
+
+ type DP_CTL_LINK_TRAIN_Array is array (DP_Info.Training_Pattern) of Word32;
+ DP_CTL_LINK_TRAIN : constant DP_CTL_LINK_TRAIN_Array :=
+ DP_CTL_LINK_TRAIN_Array'
+ (DP_Info.TP_1 => DP_CTL_LINK_TRAIN_PAT1,
+ DP_Info.TP_2 => DP_CTL_LINK_TRAIN_PAT2,
+ DP_Info.TP_3 => DP_CTL_LINK_TRAIN_PAT2,
+ DP_Info.TP_Idle => DP_CTL_LINK_TRAIN_IDLE,
+ DP_Info.TP_None => DP_CTL_LINK_TRAIN_NORMAL);
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_Training is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.DP_CTL_A,
+ Mask_Unset => DP_CTL_LINK_TRAIN_MASK,
+ Mask_Set => DP_CTL_LINK_TRAIN (DP_Info.TP_1) or
+ DP_CTL_DISPLAYPORT_ENABLE);
+ end Pre_Training;
+
+ ----------------------------------------------------------------------------
+
+ pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+ Reason => "Needed for a common interface");
+ function Max_V_Swing
+ (Port : Digital_Port)
+ return DP_Info.DP_Voltage_Swing
+ is
+ begin
+ return DP_Info.VS_Level_2;
+ end Max_V_Swing;
+
+ function Max_Pre_Emph
+ (Port : Digital_Port;
+ Train_Set : DP_Info.Train_Set)
+ return DP_Info.DP_Pre_Emph
+ is
+ begin
+ return
+ (case Train_Set.Voltage_Swing is
+ when DP_Info.VS_Level_0 => DP_Info.Emph_Level_2,
+ when DP_Info.VS_Level_1 |
+ DP_Info.VS_Level_2 => DP_Info.Emph_Level_1,
+ when others => DP_Info.Emph_Level_0);
+ end Max_Pre_Emph;
+
+ ----------------------------------------------------------------------------
+
+ pragma Warnings (GNATprove, Off, "unused variable ""Link""",
+ Reason => "Needed for a common interface");
+ procedure Set_Training_Pattern
+ (Port : Digital_Port;
+ Link : DP_Link;
+ Pattern : DP_Info.Training_Pattern)
+ is
+ use type DP_Info.Training_Pattern;
+ begin
+ if Pattern < DP_Info.TP_Idle then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.DP_CTL_A,
+ Mask_Unset => DP_CTL_LINK_TRAIN_MASK,
+ Mask_Set => DP_CTL_LINK_TRAIN (Pattern));
+ else
+ -- send at least 5 idle patterns
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.DP_CTL_A,
+ Mask_Unset => DP_CTL_LINK_TRAIN_MASK,
+ Mask_Set => DP_CTL_LINK_TRAIN (DP_Info.TP_Idle));
+
+ -- we switch to normal frame delivery later in Post_On procedure
+ end if;
+ end Set_Training_Pattern;
+
+ procedure Set_Signal_Levels
+ (Port : Digital_Port;
+ Link : DP_Link;
+ Train_Set : DP_Info.Train_Set)
+ is
+ VSwing_Emph : Word32;
+ begin
+ VSwing_Emph :=
+ (case Train_Set.Voltage_Swing is
+ when DP_Info.VS_Level_0 =>
+ (case Train_Set.Pre_Emph is
+ when DP_Info.Emph_Level_0 => DP_CTL_VSWING_0_EMPH_0,
+ when DP_Info.Emph_Level_1 => DP_CTL_VSWING_0_EMPH_1,
+ when DP_Info.Emph_Level_2 => DP_CTL_VSWING_0_EMPH_2,
+ when others => DP_CTL_VSWING_0_EMPH_0),
+ when DP_Info.VS_Level_1 =>
+ (case Train_Set.Pre_Emph is
+ when DP_Info.Emph_Level_0 => DP_CTL_VSWING_1_EMPH_0,
+ when DP_Info.Emph_Level_1 => DP_CTL_VSWING_1_EMPH_1,
+ when others => DP_CTL_VSWING_1_EMPH_0),
+ when DP_Info.VS_Level_2 =>
+ (case Train_Set.Pre_Emph is
+ when DP_Info.Emph_Level_0 => DP_CTL_VSWING_2_EMPH_0,
+ when DP_Info.Emph_Level_1 => DP_CTL_VSWING_2_EMPH_1,
+ when others => DP_CTL_VSWING_2_EMPH_0),
+ when others => DP_CTL_VSWING_0_EMPH_0);
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.DP_CTL_A,
+ Mask_Unset => DP_CTL_VSWING_EMPH_SET_MASK,
+ Mask_Set => VSwing_Emph);
+ end Set_Signal_Levels;
+ pragma Warnings (GNATprove, On, "unused variable ""Port""");
+ pragma Warnings (GNATprove, On, "unused variable ""Link""");
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_On
+ (Port_Cfg : Port_Config;
+ Pipe_Hint : Word32)
+ is
+ DP_CTL_Set : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ DP_CTL_Set :=
+ Shift_Left (Pipe_Hint, DP_CTL_PIPE_SELECT_SHIFT) or
+ DP_CTL_PORT_WIDTH (Port_Cfg.DP.Lane_Count);
+
+ if Port_Cfg.DP.Enhanced_Framing then
+ DP_CTL_Set := DP_CTL_Set or DP_CTL_ENHANCED_FRAMING_ENABLE;
+ end if;
+
+ case Port_Cfg.DP.Bandwidth is
+ when DP_Bandwidth_1_62 =>
+ DP_CTL_Set := DP_CTL_Set or DP_CTL_PLL_FREQUENCY_162;
+ when DP_Bandwidth_2_7 =>
+ DP_CTL_Set := DP_CTL_Set or DP_CTL_PLL_FREQUENCY_270;
+ when others =>
+ null;
+ end case;
+
+ if Port_Cfg.Mode.V_Sync_Active_High then
+ DP_CTL_Set := DP_CTL_Set or DP_CTL_VSYNC_ACTIVE_HIGH;
+ end if;
+ if Port_Cfg.Mode.H_Sync_Active_High then
+ DP_CTL_Set := DP_CTL_Set or DP_CTL_HSYNC_ACTIVE_HIGH;
+ end if;
+
+ Registers.Write
+ (Register => Registers.DP_CTL_A,
+ Value => DP_CTL_Set);
+
+ Registers.Write
+ (Register => Registers.DP_CTL_A,
+ Value => DP_CTL_PLL_ENABLE or DP_CTL_Set);
+ Registers.Posting_Read (Registers.DP_CTL_A);
+ Time.U_Delay (20);
+ end Pre_On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Post_On
+ (Link : in DP_Link;
+ Success : out Boolean)
+ is
+ pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+ Reason => "Needed for a common interface");
+ function To_DP (Port : Digital_Port) return DP_Port
+ is
+ begin
+ return DP_A;
+ end To_DP;
+ pragma Warnings (GNATprove, On, "unused variable ""Port""");
+ package Training is new DP_Training
+ (TPS3_Supported => False,
+ T => Digital_Port,
+ Aux_T => DP_Port,
+ Aux_Ch => DP_Aux_Ch,
+ DP_Info => DP_Info,
+ To_Aux => To_DP,
+ Max_V_Swing => Max_V_Swing,
+ Max_Pre_Emph => Max_Pre_Emph,
+ Set_Pattern => Set_Training_Pattern,
+ Set_Signal_Levels => Set_Signal_Levels,
+ Off => Off);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Training.Train_DP
+ (Port => DIGI_A,
+ Link => Link,
+ Success => Success);
+
+ if Success then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.DP_CTL_A,
+ Mask_Unset => DP_CTL_LINK_TRAIN_MASK,
+ Mask_Set => DP_CTL_LINK_TRAIN_NORMAL);
+ end if;
+ end Post_On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Off (Port : Digital_Port)
+ is
+ Enabled : Boolean;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.DP_CTL_A,
+ Mask_Unset => DP_CTL_LINK_TRAIN_MASK,
+ Mask_Set => DP_CTL_LINK_TRAIN_IDLE);
+ Registers.Posting_Read (Registers.DP_CTL_A);
+
+ Registers.Unset_Mask
+ (Register => Registers.DP_CTL_A,
+ Mask => DP_CTL_DISPLAYPORT_ENABLE);
+ -- implicit Posting_Read below
+
+ Registers.Is_Set_Mask
+ (Register => Registers.DP_CTL_A,
+ Mask => DP_CTL_PLL_ENABLE,
+ Result => Enabled);
+
+ Registers.Write
+ (Register => Registers.DP_CTL_A,
+ Value => 16#0000_0000#);
+ Registers.Posting_Read (Registers.DP_CTL_A);
+
+ if Enabled then
+ Time.U_Delay (20);
+ end if;
+ end Off;
+
+end HW.GFX.GMA.Connectors.EDP;
diff --git a/common/ironlake/hw-gfx-gma-connectors-edp.ads b/common/ironlake/hw-gfx-gma-connectors-edp.ads
new file mode 100644
index 0000000..516e093
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors-edp.ads
@@ -0,0 +1,32 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Connectors.EDP
+is
+
+ procedure Pre_On
+ (Port_Cfg : Port_Config;
+ Pipe_Hint : Word32);
+
+ procedure Post_On
+ (Link : in DP_Link;
+ Success : out Boolean);
+
+ pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+ Reason => "Needed for a common interface");
+ procedure Off (Port : Digital_Port);
+ pragma Warnings (GNATprove, On, "unused variable ""Port""");
+
+ procedure Pre_Training;
+
+end HW.GFX.GMA.Connectors.EDP;
diff --git a/common/ironlake/hw-gfx-gma-connectors-fdi.adb b/common/ironlake/hw-gfx-gma-connectors-fdi.adb
new file mode 100644
index 0000000..2d295f7
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors-fdi.adb
@@ -0,0 +1,342 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.PCH.FDI;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors.FDI
+is
+
+ PCH_FDI_CHICKEN_B_AND_C : constant := 1 * 2 ** 12;
+
+ type TX_CTL_Regs is array (GPU_FDI_Port) of Registers.Registers_Index;
+ TX_CTL : constant TX_CTL_Regs :=
+ (DIGI_B => Registers.FDI_TX_CTL_A,
+ DIGI_C => Registers.FDI_TX_CTL_B,
+ DIGI_D => Registers.FDI_TX_CTL_C);
+
+ FDI_TX_CTL_FDI_TX_ENABLE : constant := 1 * 2 ** 31;
+ FDI_TX_CTL_VP_MASK : constant := 16#3f# * 2 ** 22;
+ FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT : constant := 19;
+ FDI_TX_CTL_ENHANCED_FRAMING_ENABLE : constant := 1 * 2 ** 18;
+ FDI_TX_CTL_FDI_PLL_ENABLE : constant := 1 * 2 ** 14;
+ FDI_TX_CTL_COMPOSITE_SYNC_SELECT : constant := 1 * 2 ** 11;
+ FDI_TX_CTL_AUTO_TRAIN_ENABLE : constant := 1 * 2 ** 10;
+ FDI_TX_CTL_AUTO_TRAIN_DONE : constant := 1 * 2 ** 1;
+
+ TP_SHIFT : constant := (if Config.CPU <= Sandybridge then 28 else 8);
+ FDI_TX_CTL_TRAINING_PATTERN_MASK : constant := 3 * 2 ** TP_SHIFT;
+ FDI_TX_CTL_TRAINING_PATTERN_1 : constant := 0 * 2 ** TP_SHIFT;
+ FDI_TX_CTL_TRAINING_PATTERN_2 : constant := 1 * 2 ** TP_SHIFT;
+ FDI_TX_CTL_TRAINING_PATTERN_IDLE : constant := 2 * 2 ** TP_SHIFT;
+ FDI_TX_CTL_TRAINING_PATTERN_NORMAL : constant := 3 * 2 ** TP_SHIFT;
+
+ subtype FDI_TX_CTL_VP_T is Natural range 0 .. 3;
+ type Vswing_Preemph_Values is array (FDI_TX_CTL_VP_T) of Word32;
+ FDI_TX_CTL_VSWING_PREEMPH : constant Vswing_Preemph_Values :=
+ (0 => 16#00# * 2 ** 22,
+ 1 => 16#3a# * 2 ** 22,
+ 2 => 16#39# * 2 ** 22,
+ 3 => 16#38# * 2 ** 22);
+
+ function FDI_TX_CTL_PORT_WIDTH_SEL (Lane_Count : DP_Lane_Count) return Word32
+ is
+ begin
+ return Shift_Left
+ (Word32 (Lane_Count_As_Integer (Lane_Count)) - 1,
+ FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT);
+ end FDI_TX_CTL_PORT_WIDTH_SEL;
+
+ ----------------------------------------------------------------------------
+
+ --
+ -- This is usually used with Ivy Bridge.
+ --
+ procedure Auto_Training
+ (Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ with
+ Pre => Port_Cfg.Port in GPU_FDI_Port
+ is
+ PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ -- try each preemph/voltage pair twice
+ for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
+ loop
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_VP_MASK or
+ FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_FDI_TX_ENABLE or
+ FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
+ FDI_TX_CTL_AUTO_TRAIN_ENABLE or
+ FDI_TX_CTL_TRAINING_PATTERN_1);
+ Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+ PCH.FDI.Auto_Train (PCH_FDI_Port);
+
+ -- read at least twice
+ for I in 0 .. 3 loop
+ Registers.Is_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask => FDI_TX_CTL_AUTO_TRAIN_DONE,
+ Result => Success);
+ exit when Success or I = 3;
+ Time.U_Delay (1);
+ end loop;
+ exit when Success;
+
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
+ FDI_TX_CTL_AUTO_TRAIN_ENABLE or
+ FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_1);
+
+ PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+ end loop;
+
+ if Success then
+ PCH.FDI.Enable_EC (PCH_FDI_Port);
+ else
+ Registers.Unset_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
+
+ PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+ end if;
+ end Auto_Training;
+
+ ----------------------------------------------------------------------------
+
+ --
+ -- Used with Sandy Bridge (should work with Ivy Bridge too)
+ --
+ procedure Full_Training
+ (Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ with
+ Pre => Port_Cfg.Port in GPU_FDI_Port
+ is
+ PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ -- try each preemph/voltage pair twice
+ for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
+ loop
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_VP_MASK or
+ FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_FDI_TX_ENABLE or
+ FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
+ FDI_TX_CTL_TRAINING_PATTERN_1);
+ Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+ PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_1, Success);
+
+ if Success then
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_2);
+ Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+ PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_2, Success);
+ end if;
+ exit when Success;
+
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
+ FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_1);
+
+ PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+ end loop;
+
+ if Success then
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_NORMAL);
+ Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+ PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_None, Success);
+ else
+ Registers.Unset_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
+
+ PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+ end if;
+ end Full_Training;
+
+ ----------------------------------------------------------------------------
+
+ --
+ -- Used with original Ironlake (Nehalem CPU)
+ --
+ -- This is close to what Linux' i915 does. A comment in i915_reg.h
+ -- states that it uses only the lowest voltage / pre-emphasis levels
+ -- which is why we leave them at zero here and don't try different
+ -- values.
+ --
+ -- It's actually not clear from i915's code if the values really are
+ -- at zero or if it's just reusing what the Video BIOS set. Some code
+ -- in coreboot sets them to zero explicitly.
+ --
+ procedure Simple_Training
+ (Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ with
+ Pre => Port_Cfg.Port in GPU_FDI_Port
+ is
+ PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_FDI_TX_ENABLE or
+ FDI_TX_CTL_TRAINING_PATTERN_1);
+ Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+ PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_1, Success);
+
+ if Success then
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_2);
+ Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+ PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_2, Success);
+ end if;
+
+ if Success then
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_NORMAL);
+ Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+
+ PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_None, Success);
+ else
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
+ FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_1);
+ PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+
+ Registers.Unset_Mask
+ (Register => TX_CTL (Port_Cfg.Port),
+ Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
+ PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+ end if;
+ end Simple_Training;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_On (Port_Cfg : Port_Config)
+ is
+ Composite_Sel : constant :=
+ (if Config.Has_FDI_Composite_Sel then
+ FDI_TX_CTL_COMPOSITE_SYNC_SELECT else 0);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ -- The PCH_FDI_CHICKEN_B_AND_C bit may not be changed when any of
+ -- both ports is active. Bandwidth calculations before calling us
+ -- should ensure this.
+ if Config.Has_FDI_C then
+ if Port_Cfg.Port = DIGI_D or
+ (Port_Cfg.Port = DIGI_C and
+ Port_Cfg.FDI.Lane_Count <= DP_Lane_Count_2)
+ then
+ Registers.Set_Mask
+ (Register => Registers.PCH_FDI_CHICKEN_B_C,
+ Mask => PCH_FDI_CHICKEN_B_AND_C);
+ elsif Port_Cfg.Port = DIGI_C then
+ Registers.Unset_Mask
+ (Register => Registers.PCH_FDI_CHICKEN_B_C,
+ Mask => PCH_FDI_CHICKEN_B_AND_C);
+ end if;
+ end if;
+
+ PCH.FDI.Pre_Train (PCH_FDIs (Port_Cfg.Port), Port_Cfg);
+
+ Registers.Write
+ (Register => TX_CTL (Port_Cfg.Port),
+ Value => FDI_TX_CTL_PORT_WIDTH_SEL (Port_Cfg.FDI.Lane_Count) or
+ FDI_TX_CTL_ENHANCED_FRAMING_ENABLE or
+ FDI_TX_CTL_FDI_PLL_ENABLE or
+ Composite_Sel or
+ FDI_TX_CTL_TRAINING_PATTERN_1);
+ Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
+ Time.U_Delay (100);
+ end Pre_On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Post_On
+ (Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ case Config.FDI_Training is
+ when GMA.Simple_Training => Simple_Training (Port_Cfg, Success);
+ when GMA.Full_Training => Full_Training (Port_Cfg, Success);
+ when GMA.Auto_Training => Auto_Training (Port_Cfg, Success);
+ end case;
+ end Post_On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Off (Port : GPU_FDI_Port; OT : Off_Type)
+ is
+ PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_And_Set_Mask
+ (Register => TX_CTL (Port),
+ Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
+ FDI_TX_CTL_AUTO_TRAIN_ENABLE or
+ FDI_TX_CTL_TRAINING_PATTERN_MASK,
+ Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_1);
+
+ PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
+
+ if OT >= Clock_Off then
+ Registers.Unset_Mask
+ (Register => TX_CTL (Port),
+ Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
+
+ PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
+ end if;
+ end Off;
+
+end HW.GFX.GMA.Connectors.FDI;
diff --git a/common/ironlake/hw-gfx-gma-connectors-fdi.ads b/common/ironlake/hw-gfx-gma-connectors-fdi.ads
new file mode 100644
index 0000000..9f0cf08
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors-fdi.ads
@@ -0,0 +1,43 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.PCH;
+
+private package HW.GFX.GMA.Connectors.FDI
+is
+
+ subtype GPU_FDI_Port is GPU_Port range DIGI_B .. DIGI_D;
+
+ type PCH_FDI_Mapping is array (GPU_FDI_Port) of PCH.FDI_Port_Type;
+ PCH_FDIs : constant PCH_FDI_Mapping :=
+ (DIGI_B => PCH.FDI_A,
+ DIGI_C => PCH.FDI_B,
+ DIGI_D => PCH.FDI_C);
+
+ type Off_Type is (Link_Off, Clock_Off);
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_On (Port_Cfg : Port_Config)
+ with
+ Pre => Port_Cfg.Port in GPU_FDI_Port;
+
+ procedure Post_On
+ (Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ with
+ Pre => Port_Cfg.Port in GPU_FDI_Port;
+
+ procedure Off (Port : GPU_FDI_Port; OT : Off_Type);
+
+end HW.GFX.GMA.Connectors.FDI;
diff --git a/common/ironlake/hw-gfx-gma-connectors.adb b/common/ironlake/hw-gfx-gma-connectors.adb
new file mode 100644
index 0000000..4e3ff53
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-connectors.adb
@@ -0,0 +1,184 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+-- Copyright (C) 2016 Nico Huber <nico.h@gmx.de>
+--
+-- 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Config;
+with HW.GFX.GMA.Panel;
+with HW.GFX.GMA.Connectors.EDP;
+with HW.GFX.GMA.Connectors.FDI;
+with HW.GFX.GMA.PCH.VGA;
+with HW.GFX.GMA.PCH.LVDS;
+with HW.GFX.GMA.PCH.HDMI;
+with HW.GFX.GMA.PCH.DP;
+with HW.GFX.GMA.PCH.Transcoder;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.Connectors
+is
+
+ function Is_Internal (Port_Cfg : Port_Config) return Boolean
+ is
+ begin
+ return
+ Port_Cfg.Port = DIGI_A or
+ (Port_Cfg.Is_FDI and Port_Cfg.PCH_Port = PCH_LVDS);
+ end Is_Internal;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_On
+ (Port_Cfg : in Port_Config;
+ PLL_Hint : in Word32;
+ Pipe_Hint : in Word32;
+ Success : out Boolean)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Port = DIGI_A then
+ EDP.Pre_On (Port_Cfg, Pipe_Hint);
+ elsif Port_Cfg.Port in FDI.GPU_FDI_Port then
+ FDI.Pre_On (Port_Cfg);
+ end if;
+ Success := True;
+ end Pre_On;
+
+ procedure Post_On
+ (Port_Cfg : in Port_Config;
+ PLL_Hint : in Word32;
+ Success : out Boolean)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Port = DIGI_A then
+ EDP.Pre_Training;
+ Success := True;
+ elsif Port_Cfg.Port in FDI.GPU_FDI_Port then
+ declare
+ FDI_Port : constant PCH.FDI_Port_Type :=
+ FDI.PCH_FDIs (Port_Cfg.Port);
+ begin
+ FDI.Post_On (Port_Cfg, Success);
+
+ if Success then
+ PCH.Transcoder.On (Port_Cfg, FDI_Port, PLL_Hint);
+ if Port_Cfg.PCH_Port = PCH_DAC then
+ PCH.VGA.On (FDI_Port, Port_Cfg.Mode);
+ elsif Port_Cfg.PCH_Port = PCH_LVDS then
+ PCH.LVDS.On (Port_Cfg, FDI_Port);
+ elsif Port_Cfg.PCH_Port in PCH_HDMI_Port then
+ PCH.HDMI.On (Port_Cfg, FDI_Port);
+ elsif Port_Cfg.PCH_Port in PCH_DP_Port then
+ PCH.DP.On (Port_Cfg, Success);
+ end if;
+ end if;
+ end;
+ else
+ Success := False;
+ end if;
+
+ if Success and Is_Internal (Port_Cfg) then
+ Panel.On;
+ end if;
+
+ if Port_Cfg.Port = DIGI_A then
+ EDP.Post_On (Port_Cfg.DP, Success);
+ end if;
+
+ if Success and Is_Internal (Port_Cfg) then
+ Panel.Backlight_On;
+ end if;
+ end Post_On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_Off (Port_Cfg : Port_Config)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Is_Internal (Port_Cfg) then
+ Panel.Backlight_Off;
+ Panel.Off;
+ end if;
+ end Pre_Off;
+
+ procedure Post_Off (Port_Cfg : Port_Config)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Port = DIGI_A then
+ EDP.Off (Port_Cfg.Port);
+ elsif Port_Cfg.Port in FDI.GPU_FDI_Port then
+ declare
+ FDI_Port : constant PCH.FDI_Port_Type :=
+ FDI.PCH_FDIs (Port_Cfg.Port);
+ begin
+ if Port_Cfg.PCH_Port in PCH_DP_Port then
+ PCH.DP.Off (Port_Cfg.PCH_Port);
+ end if;
+
+ FDI.Off (Port_Cfg.Port, FDI.Link_Off);
+
+ if Port_Cfg.PCH_Port = PCH_DAC then
+ PCH.VGA.Off;
+ elsif Port_Cfg.PCH_Port = PCH_LVDS then
+ PCH.LVDS.Off;
+ elsif Port_Cfg.PCH_Port in PCH_HDMI_Port then
+ PCH.HDMI.Off (Port_Cfg.PCH_Port);
+ end if;
+ PCH.Transcoder.Off (FDI_Port);
+
+ FDI.Off (Port_Cfg.Port, FDI.Clock_Off);
+ end;
+ end if;
+ end Post_Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_All_Off
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Panel.Backlight_Off;
+ Panel.Off;
+ end Pre_All_Off;
+
+ procedure Post_All_Off
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ EDP.Off (DIGI_A);
+
+ for Port in FDI.GPU_FDI_Port loop
+ FDI.Off (Port, FDI.Link_Off);
+ end loop;
+ PCH.VGA.Off;
+ PCH.LVDS.Off;
+ PCH.HDMI.All_Off;
+ PCH.DP.All_Off;
+ for Port in PCH.FDI_Port_Type loop
+ PCH.Transcoder.Off (Port);
+ end loop;
+ for Port in FDI.GPU_FDI_Port loop
+ FDI.Off (Port, FDI.Clock_Off);
+ end loop;
+ end Post_All_Off;
+
+end HW.GFX.GMA.Connectors;
diff --git a/common/ironlake/hw-gfx-gma-pch-dp.adb b/common/ironlake/hw-gfx-gma-pch-dp.adb
new file mode 100644
index 0000000..1fb3578
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-dp.adb
@@ -0,0 +1,205 @@
+--
+-- Copyright (C) 2016 Nico Huber <nico.h@gmx.de>
+--
+-- 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; version 2 of the License.
+--
+-- 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 HW.GFX.DP_Training;
+with HW.GFX.GMA.DP_Aux_Ch;
+with HW.GFX.GMA.DP_Info;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PCH.DP is
+
+ type DP_Array is array (PCH_DP_Port) of Registers.Registers_Index;
+ DP_CTL : constant DP_Array :=
+ (PCH_DP_B => Registers.PCH_DP_B,
+ PCH_DP_C => Registers.PCH_DP_C,
+ PCH_DP_D => Registers.PCH_DP_D);
+
+ DP_CTL_DISPLAY_PORT_ENABLE : constant := 1 * 2 ** 31;
+ DP_CTL_VSWING_LEVEL_SET_SHIFT : constant := 25;
+ DP_CTL_VSWING_LEVEL_SET_MASK : constant := 7 * 2 ** 25;
+ DP_CTL_PREEMPH_LEVEL_SET_SHIFT : constant := 22;
+ DP_CTL_PREEMPH_LEVEL_SET_MASK : constant := 7 * 2 ** 22;
+ DP_CTL_PORT_WIDTH_SHIFT : constant := 19;
+ DP_CTL_PORT_REVERSAL : constant := 1 * 2 ** 15;
+ DP_CTL_LINK_TRAIN_MASK : constant := 7 * 2 ** 8;
+ DP_CTL_LINK_TRAIN_PAT1 : constant := 0 * 2 ** 8;
+ DP_CTL_LINK_TRAIN_PAT2 : constant := 1 * 2 ** 8;
+ DP_CTL_LINK_TRAIN_IDLE : constant := 2 * 2 ** 8;
+ DP_CTL_LINK_TRAIN_NORMAL : constant := 3 * 2 ** 8;
+ DP_CTL_AUDIO_OUTPUT_ENABLE : constant := 1 * 2 ** 6;
+ DP_CTL_PORT_DETECT : constant := 1 * 2 ** 2;
+
+ function DP_CTL_VSWING_LEVEL_SET
+ (VS : DP_Info.DP_Voltage_Swing)
+ return Word32
+ is
+ begin
+ return Shift_Left
+ (Word32 (DP_Info.DP_Voltage_Swing'Pos (VS)),
+ DP_CTL_VSWING_LEVEL_SET_SHIFT);
+ end DP_CTL_VSWING_LEVEL_SET;
+
+ function DP_CTL_PREEMPH_LEVEL_SET (PE : DP_Info.DP_Pre_Emph) return Word32
+ is
+ begin
+ return Shift_Left
+ (Word32 (DP_Info.DP_Pre_Emph'Pos (PE)), DP_CTL_PREEMPH_LEVEL_SET_SHIFT);
+ end DP_CTL_PREEMPH_LEVEL_SET;
+
+ function DP_CTL_PORT_WIDTH (Lane_Count : DP_Lane_Count) return Word32
+ is
+ begin
+ return Shift_Left
+ (Word32 (Lane_Count_As_Integer (Lane_Count)) - 1,
+ DP_CTL_PORT_WIDTH_SHIFT);
+ end DP_CTL_PORT_WIDTH;
+
+ type DP_CTL_LINK_TRAIN_Array is array (DP_Info.Training_Pattern) of Word32;
+ DP_CTL_LINK_TRAIN : constant DP_CTL_LINK_TRAIN_Array :=
+ (DP_Info.TP_1 => DP_CTL_LINK_TRAIN_PAT1,
+ DP_Info.TP_2 => DP_CTL_LINK_TRAIN_PAT2,
+ DP_Info.TP_3 => DP_CTL_LINK_TRAIN_PAT2,
+ DP_Info.TP_Idle => DP_CTL_LINK_TRAIN_IDLE,
+ DP_Info.TP_None => DP_CTL_LINK_TRAIN_NORMAL);
+
+ ----------------------------------------------------------------------------
+
+ pragma Warnings (GNATprove, Off, "unused variable ""Port""",
+ Reason => "Needed for a common interface");
+ function Max_V_Swing
+ (Port : PCH_DP_Port)
+ return DP_Info.DP_Voltage_Swing
+ is
+ begin
+ return DP_Info.VS_Level_3;
+ end Max_V_Swing;
+
+ function Max_Pre_Emph
+ (Port : PCH_DP_Port;
+ Train_Set : DP_Info.Train_Set)
+ return DP_Info.DP_Pre_Emph
+ is
+ begin
+ return
+ (case Train_Set.Voltage_Swing is
+ when DP_Info.VS_Level_0 => DP_Info.Emph_Level_3,
+ when DP_Info.VS_Level_1 => DP_Info.Emph_Level_2,
+ when DP_Info.VS_Level_2 => DP_Info.Emph_Level_1,
+ when DP_Info.VS_Level_3 => DP_Info.Emph_Level_0);
+ end Max_Pre_Emph;
+
+ ----------------------------------------------------------------------------
+
+ pragma Warnings (GNATprove, Off, "unused variable ""Link""",
+ Reason => "Needed for a common interface");
+ procedure Set_Training_Pattern
+ (Port : PCH_DP_Port;
+ Link : DP_Link;
+ Pattern : DP_Info.Training_Pattern)
+ is
+ begin
+ Registers.Unset_And_Set_Mask
+ (Register => DP_CTL (Port),
+ Mask_Unset => DP_CTL_LINK_TRAIN_MASK,
+ Mask_Set => DP_CTL_LINK_TRAIN (Pattern));
+ end Set_Training_Pattern;
+
+ procedure Set_Signal_Levels
+ (Port : PCH_DP_Port;
+ Link : DP_Link;
+ Train_Set : DP_Info.Train_Set)
+ is
+ begin
+ Registers.Unset_And_Set_Mask
+ (Register => DP_CTL (Port),
+ Mask_Unset => DP_CTL_VSWING_LEVEL_SET_MASK or
+ DP_CTL_PREEMPH_LEVEL_SET_MASK,
+ Mask_Set => DP_CTL_VSWING_LEVEL_SET (Train_Set.Voltage_Swing) or
+ DP_CTL_PREEMPH_LEVEL_SET (Train_Set.Pre_Emph));
+ end Set_Signal_Levels;
+
+ procedure Off (Port : PCH_DP_Port)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_And_Set_Mask
+ (Register => DP_CTL (Port),
+ Mask_Unset => DP_CTL_LINK_TRAIN_MASK,
+ Mask_Set => DP_CTL_LINK_TRAIN_IDLE);
+ Registers.Posting_Read (DP_CTL (Port));
+
+ Registers.Write (DP_CTL (Port), 0);
+ Registers.Posting_Read (DP_CTL (Port));
+ end Off;
+ pragma Warnings (GNATprove, On, "unused variable ""Port""");
+ pragma Warnings (GNATprove, On, "unused variable ""Link""");
+
+ ----------------------------------------------------------------------------
+
+ procedure On
+ (Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ is
+ function To_DP (Port : PCH_DP_Port) return DP_Port
+ is
+ begin
+ return
+ (case Port is
+ when PCH_DP_B => DP_B,
+ when PCH_DP_C => DP_C,
+ when PCH_DP_D => DP_D);
+ end To_DP;
+ package Training is new DP_Training
+ (TPS3_Supported => False,
+ T => PCH_DP_Port,
+ Aux_T => DP_Port,
+ Aux_Ch => DP_Aux_Ch,
+ DP_Info => DP_Info,
+ To_Aux => To_DP,
+ Max_V_Swing => Max_V_Swing,
+ Max_Pre_Emph => Max_Pre_Emph,
+ Set_Pattern => Set_Training_Pattern,
+ Set_Signal_Levels => Set_Signal_Levels,
+ Off => Off);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Write
+ (Register => DP_CTL (Port_Cfg.PCH_Port),
+ Value => DP_CTL_DISPLAY_PORT_ENABLE or
+ DP_CTL_PORT_WIDTH (Port_Cfg.DP.Lane_Count) or
+ DP_CTL_LINK_TRAIN_PAT1);
+
+ Training.Train_DP
+ (Port => Port_Cfg.PCH_Port,
+ Link => Port_Cfg.DP,
+ Success => Success);
+ end On;
+
+ ----------------------------------------------------------------------------
+
+ procedure All_Off
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ for Port in PCH_DP_Port loop
+ Off (Port);
+ end loop;
+ end All_Off;
+
+end HW.GFX.GMA.PCH.DP;
diff --git a/common/ironlake/hw-gfx-gma-pch-dp.ads b/common/ironlake/hw-gfx-gma-pch-dp.ads
new file mode 100644
index 0000000..ee77e30
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-dp.ads
@@ -0,0 +1,26 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.DP
+is
+
+ procedure On
+ (Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ with
+ Pre => Port_Cfg.PCH_Port in PCH_DP_Port;
+
+ procedure Off (Port : PCH_DP_Port);
+ procedure All_Off;
+
+end HW.GFX.GMA.PCH.DP;
diff --git a/common/ironlake/hw-gfx-gma-pch-hdmi.adb b/common/ironlake/hw-gfx-gma-pch-hdmi.adb
new file mode 100644
index 0000000..8bf99db
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-hdmi.adb
@@ -0,0 +1,94 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PCH.HDMI
+is
+
+ PCH_HDMI_ENABLE : constant := 1 * 2 ** 31;
+ PCH_HDMI_COLOR_FORMAT_8BPC : constant := 0 * 2 ** 26;
+ PCH_HDMI_COLOR_FORMAT_12BPC : constant := 3 * 2 ** 26;
+ PCH_HDMI_COLOR_FORMAT_MASK : constant := 7 * 2 ** 26;
+ PCH_HDMI_SDVO_ENCODING_SDVO : constant := 0 * 2 ** 10;
+ PCH_HDMI_SDVO_ENCODING_HDMI : constant := 2 * 2 ** 10;
+ PCH_HDMI_SDVO_ENCODING_MASK : constant := 3 * 2 ** 10;
+ PCH_HDMI_VSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 4;
+ PCH_HDMI_HSYNC_ACTIVE_HIGH : constant := 1 * 2 ** 3;
+ PCH_HDMI_PORT_DETECT : constant := 1 * 2 ** 2;
+
+ PCH_HDMI_MASK : constant Word32 :=
+ PCH_TRANSCODER_SELECT_MASK or
+ PCH_HDMI_ENABLE or
+ PCH_HDMI_COLOR_FORMAT_MASK or
+ PCH_HDMI_SDVO_ENCODING_MASK or
+ PCH_HDMI_HSYNC_ACTIVE_HIGH or
+ PCH_HDMI_VSYNC_ACTIVE_HIGH;
+
+ type PCH_HDMI_Array is array (PCH_HDMI_Port) of Registers.Registers_Index;
+ PCH_HDMI : constant PCH_HDMI_Array := PCH_HDMI_Array'
+ (PCH_HDMI_B => Registers.PCH_HDMIB,
+ PCH_HDMI_C => Registers.PCH_HDMIC,
+ PCH_HDMI_D => Registers.PCH_HDMID);
+
+ ----------------------------------------------------------------------------
+
+ procedure On (Port_Cfg : Port_Config; FDI_Port : FDI_Port_Type)
+ is
+ Polarity : constant Word32 :=
+ (if Port_Cfg.Mode.H_Sync_Active_High then
+ PCH_HDMI_HSYNC_ACTIVE_HIGH else 0) or
+ (if Port_Cfg.Mode.V_Sync_Active_High then
+ PCH_HDMI_VSYNC_ACTIVE_HIGH else 0);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ -- registers are just sufficient for setup with DVI adaptor
+
+ Registers.Unset_And_Set_Mask
+ (Register => PCH_HDMI (Port_Cfg.PCH_Port),
+ Mask_Unset => PCH_HDMI_MASK,
+ Mask_Set => PCH_HDMI_ENABLE or
+ PCH_TRANSCODER_SELECT (FDI_Port) or
+ PCH_HDMI_SDVO_ENCODING_HDMI or
+ Polarity);
+ end On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Off (Port : PCH_HDMI_Port)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_And_Set_Mask
+ (Register => PCH_HDMI (Port),
+ Mask_Unset => PCH_HDMI_MASK,
+ Mask_Set => PCH_HDMI_HSYNC_ACTIVE_HIGH or
+ PCH_HDMI_VSYNC_ACTIVE_HIGH);
+ end Off;
+
+ procedure All_Off
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ for Port in PCH_HDMI_Port loop
+ Off (Port);
+ end loop;
+ end All_Off;
+
+end HW.GFX.GMA.PCH.HDMI;
diff --git a/common/ironlake/hw-gfx-gma-pch-hdmi.ads b/common/ironlake/hw-gfx-gma-pch-hdmi.ads
new file mode 100644
index 0000000..9853610
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-hdmi.ads
@@ -0,0 +1,24 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.HDMI
+is
+
+ procedure On (Port_Cfg : Port_Config; FDI_Port : FDI_Port_Type)
+ with
+ Pre => Port_Cfg.PCH_Port in PCH_HDMI_Port;
+
+ procedure Off (Port : PCH_HDMI_Port);
+ procedure All_Off;
+
+end HW.GFX.GMA.PCH.HDMI;
diff --git a/common/ironlake/hw-gfx-gma-pch-lvds.adb b/common/ironlake/hw-gfx-gma-pch-lvds.adb
new file mode 100644
index 0000000..6ad334e
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-lvds.adb
@@ -0,0 +1,58 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PCH.LVDS is
+
+ PCH_LVDS_ENABLE : constant := 1 * 2 ** 31;
+ PCH_LVDS_TWO_CHANNEL : constant := 15 * 2 ** 2;
+
+ PCH_LVDS_MASK : constant Word32 :=
+ PCH_TRANSCODER_SELECT_MASK or
+ PCH_LVDS_ENABLE or
+ PCH_LVDS_TWO_CHANNEL;
+
+ ----------------------------------------------------------------------------
+
+ procedure On (Port_Cfg : Port_Config; FDI_Port : FDI_Port_Type)
+ is
+ Two_Channel : constant Word32 :=
+ (if Port_Cfg.Mode.Dotclock >= Config.LVDS_Dual_Threshold then
+ PCH_LVDS_TWO_CHANNEL else 0);
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.PCH_LVDS,
+ Mask_Unset => PCH_LVDS_MASK,
+ Mask_Set => PCH_LVDS_ENABLE or
+ PCH_TRANSCODER_SELECT (FDI_Port) or
+ Two_Channel);
+ end On;
+
+ ----------------------------------------------------------------------------
+
+ procedure Off
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Unset_Mask (Registers.PCH_LVDS, PCH_LVDS_ENABLE);
+ end Off;
+
+end HW.GFX.GMA.PCH.LVDS;
diff --git a/common/ironlake/hw-gfx-gma-pch-lvds.ads b/common/ironlake/hw-gfx-gma-pch-lvds.ads
new file mode 100644
index 0000000..f4a5f26
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-pch-lvds.ads
@@ -0,0 +1,21 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.PCH.LVDS
+is
+
+ procedure On (Port_Cfg : Port_Config; FDI_Port : FDI_Port_Type);
+
+ procedure Off;
+
+end HW.GFX.GMA.PCH.LVDS;
diff --git a/common/ironlake/hw-gfx-gma-plls.adb b/common/ironlake/hw-gfx-gma-plls.adb
new file mode 100644
index 0000000..7ecfb66
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-plls.adb
@@ -0,0 +1,570 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PLLs
+with
+ Refined_State => (State => PLLs)
+is
+
+ Debug_Clocks : constant Boolean := False;
+
+ type Count_Range is new Natural range 0 .. 2;
+
+ type PLL_State is record
+ Use_Count : Count_Range;
+ Used_For_DP : Boolean;
+ Link_Rate : DP_Bandwidth;
+ Mode : Mode_Type;
+ end record;
+
+ type PLL_State_Array is array (DPLLs) of PLL_State;
+
+ PLLs : PLL_State_Array;
+
+ ----------------------------------------------------------------------------
+
+ subtype N_Range is Int64 range 3 .. 8;
+ subtype M_Range is Int64 range 79 .. 128;
+ subtype M1_Range is Int64 range 14 .. 25;
+ subtype M2_Range is Int64 range 7 .. 12;
+ subtype P_Range is Int64 range 5 .. 112;
+ subtype P1_Range is Int64 range 1 .. 8;
+ subtype P2_Range is Int64 range 5 .. 14;
+ subtype VCO_Range is Int64 range 1760000000 .. 3510000000;
+ subtype Clock_Range is HW.GFX.Frequency_Type;
+
+ type Clock_Type is
+ record
+ N : N_Range;
+ M1 : M1_Range;
+ M2 : M2_Range;
+ P1 : P1_Range;
+ P2 : P2_Range;
+ M : M_Range;
+ P : P_Range;
+ VCO : VCO_Range;
+ Reference_Clock : Clock_Range;
+ Dotclock : Clock_Range;
+ end record;
+
+ Invalid_Clock : constant Clock_Type := Clock_Type'
+ (N => N_Range'Last,
+ M1 => M1_Range'Last,
+ M2 => M2_Range'Last,
+ P1 => P1_Range'Last,
+ P2 => P2_Range'Last,
+ Reference_Clock => Clock_Range'Last,
+ M => M_Range'Last,
+ P => P_Range'Last,
+ VCO => VCO_Range'Last,
+ Dotclock => Clock_Range'Last);
+
+ type Limits_Type is
+ record
+ N_Lower : N_Range;
+ N_Upper : N_Range;
+ M_Lower : M_Range;
+ M_Upper : M_Range;
+ M1_Lower : M1_Range;
+ M1_Upper : M1_Range;
+ M2_Lower : M2_Range;
+ M2_Upper : M2_Range;
+ P_Lower : P_Range;
+ P_Upper : P_Range;
+ P1_Lower : P1_Range;
+ P1_Upper : P1_Range;
+ P2_Fast : P2_Range;
+ P2_Slow : P2_Range;
+ P2_Threshold : Clock_Range;
+ VCO_Lower : VCO_Range;
+ VCO_Upper : VCO_Range;
+ end record;
+
+ LVDS_Single_Limits : constant Limits_Type := Limits_Type'
+ (N_Lower => 3, N_Upper => 5,
+ M_Lower => 79, M_Upper => 118,
+ M1_Lower => 14, M1_Upper => 22, -- this is capped by M_Upper >= 5 * M1 + M2_Lower
+ M2_Lower => 7, M2_Upper => 11,
+ P_Lower => 28, P_Upper => 112,
+ P1_Lower => 2, P1_Upper => 8,
+ P2_Fast => 14, P2_Slow => 14,
+ P2_Threshold => Clock_Range'First,
+ VCO_Lower => 1_760_000_000, VCO_Upper => 3_510_000_000);
+ LVDS_Dual_Limits : constant Limits_Type := Limits_Type'
+ (N_Lower => 3, N_Upper => 5,
+ M_Lower => 79, M_Upper => 127,
+ M1_Lower => 14, M1_Upper => 24,
+ M2_Lower => 7, M2_Upper => 11,
+ P_Lower => 14, P_Upper => 56,
+ P1_Lower => 2, P1_Upper => 8,
+ P2_Fast => 7, P2_Slow => 7,
+ P2_Threshold => Clock_Range'First,
+ VCO_Lower => 1_760_000_000, VCO_Upper => 3_510_000_000);
+ All_Other_Limits : constant Limits_Type := Limits_Type'
+ (N_Lower => 3, N_Upper => 7,
+ M_Lower => 79, M_Upper => 127,
+ M1_Lower => 14, M1_Upper => 24,
+ M2_Lower => 7, M2_Upper => 11,
+ P_Lower => 5, P_Upper => 80,
+ P1_Lower => 1, P1_Upper => 8,
+ -- use P2_Slow if Dotclock <= P2_Threshold, P2_Fast otherwise
+ P2_Fast => 5, P2_Slow => 10,
+ P2_Threshold => 225_000_000,
+ VCO_Lower => 1_760_000_000, VCO_Upper => 3_510_000_000);
+
+ ----------------------------------------------------------------------------
+
+ type Regs is array (DPLLs) of Registers.Registers_Index;
+
+ DPLL : constant Regs := Regs'(Registers.PCH_DPLL_A, Registers.PCH_DPLL_B);
+ DPLL_VCO_ENABLE : constant := 1 * 2 ** 31;
+ DPLL_P2_10_OR_14 : constant := 0 * 2 ** 24;
+ DPLL_P2_5_OR_7 : constant := 1 * 2 ** 24;
+ DPLL_P1_DIVIDER_SHIFT : constant := 16;
+ DPLL_SDVOCLK : constant := 2 * 2 ** 13;
+
+ DPLL_HIGH_SPEED : constant := 1 * 2 ** 30;
+ DPLL_MODE_LVDS : constant := 2 * 2 ** 26;
+ DPLL_MODE_DAC : constant := 1 * 2 ** 26;
+ DPLL_DREFCLK : constant := 0 * 2 ** 13;
+ DPLL_SSC : constant := 3 * 2 ** 13;
+
+ MODE_DPLL_DAC_HDMI : constant Word32 := Word32'
+ (DPLL_MODE_DAC or DPLL_DREFCLK or DPLL_HIGH_SPEED);
+
+ MODE_DPLL_LVDS : constant Word32 := Word32'
+ (DPLL_MODE_LVDS or DPLL_SSC);
+
+ MODE_DPLL_DP : constant Word32 := Word32'
+ (DPLL_MODE_DAC or DPLL_SSC or DPLL_HIGH_SPEED);
+
+ type DPLL_Mode_Array is array (Display_Type) of Word32;
+
+ DPLL_Mode : constant DPLL_Mode_Array := DPLL_Mode_Array'
+ (LVDS => MODE_DPLL_LVDS,
+ DP => MODE_DPLL_DP,
+ others => MODE_DPLL_DAC_HDMI);
+
+ FP0 : constant Regs := Regs'(Registers.PCH_FPA0, Registers.PCH_FPB0);
+ FP1 : constant Regs := Regs'(Registers.PCH_FPA1, Registers.PCH_FPB1);
+ FP_DOUBLE_CLOCK : constant := 1 * 2 ** 27;
+ FP_N_SHIFT : constant := 16;
+ FP_M1_SHIFT : constant := 8;
+ FP_M2_SHIFT : constant := 0;
+
+ ----------------------------------------------------------------------------
+
+ procedure Verify_Parameters
+ (N : in N_Range;
+ M1 : in M1_Range;
+ M2 : in M2_Range;
+ P1 : in P1_Range;
+ P2 : in P2_Range;
+ Reference_Clock : in Clock_Range;
+ Current_Limits : in Limits_Type;
+ Result : out Clock_Type;
+ Valid : out Boolean)
+ with
+ Global => null,
+ Pre => True,
+ Post => True
+ is
+ M : Int64;
+ P : Int64;
+ VCO : Int64;
+ Dotclock : Int64;
+ begin
+ pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ M := 5 * M1 + M2;
+ P := P1 * P2;
+ VCO := (Int64 (Reference_Clock) * M) / N;
+ Dotclock := VCO / P;
+
+ pragma Debug (Debug_Clocks and not (Current_Limits.P1_Lower <= P1 and P1 <= Current_Limits.P1_Upper ), Debug.Put_Line ("P1 out of range."));
+ pragma Debug (Debug_Clocks and (Current_Limits.P2_Fast /= P2 and P2 /= Current_Limits.P2_Slow ), Debug.Put_Line ("P2 out of range."));
+ pragma Debug (Debug_Clocks and not (Current_Limits.P_Lower <= P and P <= Current_Limits.P_Upper ), Debug.Put_Line ("P out of range."));
+ pragma Debug (Debug_Clocks and not (Current_Limits.M1_Lower <= M1 and M1 <= Current_Limits.M1_Upper ), Debug.Put_Line ("M1 out of range."));
+ pragma Debug (Debug_Clocks and not (Current_Limits.M2_Lower <= M2 and M2 <= Current_Limits.M2_Upper ), Debug.Put_Line ("M2 out of range."));
+ -- pragma Debug (Debug_Clocks and not (M2 <= M1 ), Debug.Put_Line ("M1 greater thant M2."));
+ pragma Debug (Debug_Clocks and not (Current_Limits.N_Lower <= N and N <= Current_Limits.N_Upper ), Debug.Put_Line ("N out of range."));
+ pragma Debug (Debug_Clocks and not (Current_Limits.M_Lower <= M and M <= Current_Limits.M_Upper ), Debug.Put_Line ("M out of range."));
+ pragma Debug (Debug_Clocks and not (Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper), Debug.Put_Line ("VCO out of range."));
+
+ pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock), Debug.Put_Line ("Dotclock too low."));
+ pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock), Debug.Put_Int64 (Dotclock));
+ pragma Debug (Debug_Clocks and not (Int64 (Clock_Range'First) <= Dotclock), Debug.New_Line);
+
+ pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)), Debug.Put_Line ("Dotclock too high."));
+ pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)), Debug.Put_Int64 (Dotclock));
+ pragma Debug (Debug_Clocks and not (Dotclock <= Int64 (Clock_Range'Last)), Debug.New_Line);
+
+ Valid :=
+ Current_Limits.P1_Lower <= P1 and P1 <= Current_Limits.P1_Upper and
+ (Current_Limits.P2_Fast = P2 or P2 = Current_Limits.P2_Slow) and
+ Current_Limits.P_Lower <= P and P <= Current_Limits.P_Upper and
+ Current_Limits.M1_Lower <= M1 and M1 <= Current_Limits.M1_Upper and
+ Current_Limits.M2_Lower <= M2 and M2 <= Current_Limits.M2_Upper and
+ -- M2 <= M1 and
+ Current_Limits.N_Lower <= N and N <= Current_Limits.N_Upper and
+ Current_Limits.M_Lower <= M and M <= Current_Limits.M_Upper and
+ Current_Limits.VCO_Lower <= VCO and VCO <= Current_Limits.VCO_Upper and
+ Int64 (Clock_Range'First) <= Dotclock and
+ Dotclock <= Int64 (Clock_Range'Last);
+
+ if Valid
+ then
+ Result := Clock_Type'
+ (N => N,
+ M1 => M1,
+ M2 => M2,
+ P1 => P1,
+ P2 => P2,
+ Reference_Clock => Reference_Clock,
+ M => M,
+ P => P,
+ VCO => VCO,
+ Dotclock => Clock_Range (Dotclock));
+ else
+ Result := Invalid_Clock;
+ end if;
+
+ end Verify_Parameters;
+
+ procedure Calculate_Clock_Parameters
+ (Display : in Display_Type;
+ Target_Dotclock : in Clock_Range;
+ Reference_Clock : in Clock_Range;
+ Best_Clock : out Clock_Type;
+ Valid : out Boolean)
+ with
+ Global => null,
+ Pre => True,
+ Post => True
+ is
+ Limits : constant Limits_Type :=
+ (if Display = LVDS then
+ (if Target_Dotclock >= Config.LVDS_Dual_Threshold then
+ LVDS_Dual_Limits
+ else
+ LVDS_Single_Limits)
+ else
+ All_Other_Limits);
+
+ P2 : P2_Range;
+ Best_Delta : Int64 := Int64'Last;
+ Current_Delta : Int64;
+ Current_Clock : Clock_Type;
+ Registers_Valid : Boolean;
+ begin
+ pragma Debug (Debug_Clocks, Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Valid := False;
+ Best_Clock := Invalid_Clock;
+
+ if Target_Dotclock <= Limits.P2_Threshold then
+ P2 := Limits.P2_Slow;
+ else
+ P2 := Limits.P2_Fast;
+ end if;
+
+ for N in N_Range range Limits.N_Lower .. Limits.N_Upper
+ loop
+ -- reverse loops as hardware prefers higher values
+ for M1 in reverse M1_Range range Limits.M1_Lower .. Limits.M1_Upper
+ loop
+ for M2 in reverse M2_Range range Limits.M2_Lower .. Limits.M2_Upper
+ loop
+ for P1 in reverse P1_Range range Limits.P1_Lower .. Limits.P1_Upper
+ loop
+ Verify_Parameters
+ (N => N,
+ M1 => M1,
+ M2 => M2,
+ P1 => P1,
+ P2 => P2,
+ Reference_Clock => Reference_Clock,
+ Current_Limits => Limits,
+ Result => Current_Clock,
+ Valid => Registers_Valid);
+
+ if Registers_Valid
+ then
+ if Current_Clock.Dotclock > Target_Dotclock
+ then
+ Current_Delta := Current_Clock.Dotclock - Target_Dotclock;
+ else
+ Current_Delta := Target_Dotclock - Current_Clock.Dotclock;
+ end if;
+
+ if Current_Delta < Best_Delta
+ then
+ Best_Delta := Current_Delta;
+ Best_Clock := Current_Clock;
+ Valid := True;
+ end if;
+
+ pragma Debug (Debug_Clocks, Debug.Put ("Current/Target/Best_Delta: "));
+ pragma Debug (Debug_Clocks, Debug.Put_Int64 (Current_Clock.Dotclock));
+ pragma Debug (Debug_Clocks, Debug.Put ("/"));
+ pragma Debug (Debug_Clocks, Debug.Put_Int64 (Target_Dotclock));
+ pragma Debug (Debug_Clocks, Debug.Put ("/"));
+ pragma Debug (Debug_Clocks, Debug.Put_Int64 (Best_Delta));
+ pragma Debug (Debug_Clocks, Debug.Put_Line ("."));
+
+ end if;
+ end loop;
+ end loop;
+ end loop;
+ end loop;
+
+ pragma Debug (Valid, Debug.Put_Line ("Valid clock found."));
+ pragma Debug (Valid, Debug.Put ("Best/Target/Delta: "));
+ pragma Debug (Valid, Debug.Put_Int64 (Best_Clock.Dotclock));
+ pragma Debug (Valid, Debug.Put ("/"));
+ pragma Debug (Valid, Debug.Put_Int64 (Target_Dotclock));
+ pragma Debug (Valid, Debug.Put ("/"));
+ pragma Debug (Valid, Debug.Put_Int64 (Best_Delta));
+ pragma Debug (Valid, Debug.Put_Line ("."));
+ pragma Debug (not Valid, Debug.Put_Line ("No valid clock found."));
+
+ end Calculate_Clock_Parameters;
+
+ procedure Program_DPLL
+ (PLL : DPLLs;
+ Display : Display_Type;
+ Clk : Clock_Type)
+ with
+ Global => (In_Out => Registers.Register_State),
+ Pre => True,
+ Post => True
+ is
+ FP, Encoded_P1, Encoded_P2 : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ FP :=
+ Shift_Left (Word32 (Clk.N - 2), FP_N_SHIFT) or
+ Shift_Left (Word32 (Clk.M1 - 2), FP_M1_SHIFT) or
+ Shift_Left (Word32 (Clk.M2 - 2), FP_M2_SHIFT);
+
+ Registers.Write (FP0 (PLL), FP);
+ Registers.Write (FP1 (PLL), FP);
+
+ Encoded_P1 := Shift_Left (1, Natural (Clk.P1) - 1);
+
+ if Clk.P2 = 5 or Clk.P2 = 7
+ then
+ Encoded_P2 := DPLL_P2_5_OR_7;
+ else
+ Encoded_P2 := DPLL_P2_10_OR_14;
+ end if;
+
+ Registers.Write
+ (Register => DPLL (PLL),
+ Value => DPLL_Mode (Display) or
+ Encoded_P2 or
+ Shift_Left (Encoded_P1, DPLL_P1_DIVIDER_SHIFT) or
+ Encoded_P1);
+ end Program_DPLL;
+
+ procedure On
+ (PLL : in T;
+ Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ is
+ Target_Clock : constant Frequency_Type :=
+ (if Port_Cfg.Display = DP then
+ DP_Symbol_Rate (Port_Cfg.DP.Bandwidth)
+ else
+ Port_Cfg.Mode.Dotclock);
+ Clk : Clock_Type;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Success := PLL in DPLLs;
+ Clk := Invalid_Clock;
+
+ if Success then
+ if Port_Cfg.Display = DP then
+ Success := True;
+ -- we use static values for DP
+ case Port_Cfg.DP.Bandwidth is
+ when DP_Bandwidth_1_62 =>
+ Clk.N := 3;
+ Clk.M1 := 14;
+ Clk.M2 := 11;
+ Clk.P1 := 2;
+ Clk.P2 := 10;
+ when DP_Bandwidth_2_7 =>
+ Clk.N := 4;
+ Clk.M1 := 16;
+ Clk.M2 := 10;
+ Clk.P1 := 1;
+ Clk.P2 := 10;
+ when others =>
+ Success := False;
+ end case;
+ elsif Target_Clock <= 340_000_000 then
+ Calculate_Clock_Parameters
+ (Display => Port_Cfg.Display,
+ Target_Dotclock => Target_Clock,
+ -- should be, but doesn't has to be always the same:
+ Reference_Clock => 120_000_000,
+ Best_Clock => Clk,
+ Valid => Success);
+ else
+ Success := False;
+ pragma Debug (Debug.Put ("WARNING: Targeted clock too high: "));
+ pragma Debug (Debug.Put_Int64 (Target_Clock));
+ pragma Debug (Debug.Put (" > "));
+ pragma Debug (Debug.Put_Int32 (340_000_000));
+ pragma Debug (Debug.New_Line);
+ pragma Debug (Debug.New_Line);
+ end if;
+ end if;
+
+ if Success then
+ Program_DPLL (PLL, Port_Cfg.Display, Clk);
+
+ Registers.Set_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
+ Registers.Posting_Read (DPLL (PLL));
+ Time.U_Delay (150);
+ end if;
+ end On;
+
+ procedure Off (PLL : T)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if PLL in DPLLs then
+ Registers.Unset_Mask (DPLL (PLL), DPLL_VCO_ENABLE);
+ end if;
+ end Off;
+
+ ----------------------------------------------------------------------------
+
+ procedure Initialize
+ is
+ begin
+ PLLs :=
+ (DPLLs =>
+ (Use_Count => 0,
+ Used_For_DP => False,
+ Link_Rate => DP_Bandwidth'First,
+ Mode => Invalid_Mode));
+ end Initialize;
+
+ procedure Alloc_Configurable
+ (Port_Cfg : in Port_Config;
+ PLL : out T;
+ Success : out Boolean)
+ with
+ Pre => True
+ is
+ function Config_Matches (PE : PLL_State) return Boolean
+ is
+ begin
+ return
+ PE.Used_For_DP = (Port_Cfg.Display = DP) and
+ ((PE.Used_For_DP and PE.Link_Rate = Port_Cfg.DP.Bandwidth) or
+ (not PE.Used_For_DP and PE.Mode = Port_Cfg.Mode));
+ end Config_Matches;
+ begin
+ -- try to find shareable PLL
+ for P in DPLLs loop
+ Success := PLLs (P).Use_Count /= 0 and
+ PLLs (P).Use_Count /= Count_Range'Last and
+ Config_Matches (PLLs (P));
+ if Success then
+ PLL := P;
+ PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1;
+ return;
+ end if;
+ end loop;
+
+ -- try to find free PLL
+ for P in DPLLs loop
+ if PLLs (P).Use_Count = 0 then
+ PLL := P;
+ On (PLL, Port_Cfg, Success);
+ if Success then
+ PLLs (PLL) :=
+ (Use_Count => 1,
+ Used_For_DP => Port_Cfg.Display = DP,
+ Link_Rate => Port_Cfg.DP.Bandwidth,
+ Mode => Port_Cfg.Mode);
+ end if;
+ return;
+ end if;
+ end loop;
+
+ PLL := Invalid;
+ end Alloc_Configurable;
+
+ procedure Alloc
+ (Port_Cfg : in Port_Config;
+ PLL : out T;
+ Success : out Boolean)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Port = DIGI_A then
+ PLL := Invalid;
+ Success := True;
+ else
+ Alloc_Configurable (Port_Cfg, PLL, Success);
+ end if;
+ end Alloc;
+
+ procedure Free (PLL : T)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if PLL in DPLLs then
+ if PLLs (PLL).Use_Count /= 0 then
+ PLLs (PLL).Use_Count := PLLs (PLL).Use_Count - 1;
+ if PLLs (PLL).Use_Count = 0 then
+ Off (PLL);
+ end if;
+ end if;
+ end if;
+ end Free;
+
+ procedure All_Off
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ for PLL in DPLLs loop
+ Off (PLL);
+ end loop;
+ end All_Off;
+
+ function Register_Value (PLL : T) return Word32
+ is
+ begin
+ return (if PLL = DPLL_B then 1 else 0);
+ end Register_Value;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/ironlake/hw-gfx-gma-plls.ads b/common/ironlake/hw-gfx-gma-plls.ads
new file mode 100644
index 0000000..8e7325e
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-plls.ads
@@ -0,0 +1,39 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs
+with
+ Abstract_State => (State with Part_Of => GMA.State)
+is
+
+ -- XXX: Types should be private (but that triggers a bug in SPARK GPL 2016)
+ type T is (Invalid_PLL, DPLL_A, DPLL_B);
+ subtype DPLLs is T range DPLL_A .. DPLL_B;
+ Invalid : constant T := Invalid_PLL;
+
+ procedure Initialize
+ with
+ Global => (Output => State);
+
+ procedure Alloc
+ (Port_Cfg : in Port_Config;
+ PLL : out T;
+ Success : out Boolean);
+
+ procedure Free (PLL : T);
+
+ procedure All_Off;
+
+ function Register_Value (PLL : T) return Word32;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/ironlake/hw-gfx-gma-port_detect.adb b/common/ironlake/hw-gfx-gma-port_detect.adb
new file mode 100644
index 0000000..4e26100
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-port_detect.adb
@@ -0,0 +1,160 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Port_Detect
+is
+
+ PCH_ADPA_CRT_HPD_CHANNEL_MASK : constant := 3 * 2 ** 24;
+ PCH_ADPA_CRT_HPD_ENABLE : constant := 1 * 2 ** 23;
+
+ DP_PORT_DETECTED : constant := 1 * 2 ** 2;
+ PCH_DIGI_PORT_DETECTED : constant := 1 * 2 ** 2;
+ PCH_LVDS_PORT_DETECTED : constant := 1 * 2 ** 1;
+
+ SHOTPLUG_CTL_DETECT_MASK : constant := 16#0003_0303#;
+
+ type PCH_Digital_Port_Value is array (PCH_HDMI_Port) of Word32;
+ SHOTPLUG_CTL_HPD_INPUT_ENABLE : constant PCH_Digital_Port_Value :=
+ (PCH_HDMI_B => 1 * 2 ** 4,
+ PCH_HDMI_C => 1 * 2 ** 12,
+ PCH_HDMI_D => 1 * 2 ** 20);
+ SHOTPLUG_CTL_SHORT_PULSE_MASK : constant PCH_Digital_Port_Value :=
+ (PCH_HDMI_B => 3 * 2 ** 2,
+ PCH_HDMI_C => 3 * 2 ** 10,
+ PCH_HDMI_D => 3 * 2 ** 18);
+ SHOTPLUG_CTL_HPD_STATUS : constant PCH_Digital_Port_Value :=
+ (PCH_HDMI_B => 3 * 2 ** 0,
+ PCH_HDMI_C => 3 * 2 ** 8,
+ PCH_HDMI_D => 3 * 2 ** 16);
+ SHOTPLUG_CTL_LONG_DETECT : constant PCH_Digital_Port_Value :=
+ (PCH_HDMI_B => 1 * 2 ** 1,
+ PCH_HDMI_C => 1 * 2 ** 9,
+ PCH_HDMI_D => 1 * 2 ** 17);
+
+ type PCH_Digital_Regs is array (PCH_HDMI_Port) of Registers.Registers_Index;
+ PCH_HDMI : constant PCH_Digital_Regs :=
+ (PCH_HDMI_B => Registers.PCH_HDMIB,
+ PCH_HDMI_C => Registers.PCH_HDMIC,
+ PCH_HDMI_D => Registers.PCH_HDMID);
+ PCH_DP : constant PCH_Digital_Regs :=
+ (PCH_HDMI_B => Registers.PCH_DP_B,
+ PCH_HDMI_C => Registers.PCH_DP_C,
+ PCH_HDMI_D => Registers.PCH_DP_D);
+
+ procedure Initialize
+ is
+ Internal_Detected,
+ HDMI_Detected,
+ DP_Detected : Boolean;
+
+ type PCH_Port_To_GMA_Port is array (PCH_HDMI_Port) of Port_Type;
+ To_Digital_Port : constant PCH_Port_To_GMA_Port :=
+ (PCH_HDMI_B => Digital1,
+ PCH_HDMI_C => Digital2,
+ PCH_HDMI_D => Digital3);
+ To_DP_Port : constant PCH_Port_To_GMA_Port :=
+ (PCH_HDMI_B => DP1,
+ PCH_HDMI_C => DP2,
+ PCH_HDMI_D => DP3);
+ begin
+ -- PCH_DAC (_A)
+ Registers.Set_Mask
+ (Register => Registers.PCH_ADPA,
+ Mask => PCH_ADPA_CRT_HPD_CHANNEL_MASK or -- clear status
+ PCH_ADPA_CRT_HPD_ENABLE);
+
+ case Config.Internal_Display is
+ when LVDS =>
+ -- PCH_LVDS
+ Registers.Is_Set_Mask
+ (Register => Registers.PCH_LVDS,
+ Mask => PCH_LVDS_PORT_DETECTED,
+ Result => Internal_Detected);
+ when DP =>
+ -- eDP
+ Registers.Is_Set_Mask
+ (Register => Registers.DP_CTL_A,
+ Mask => DP_PORT_DETECTED,
+ Result => Internal_Detected);
+ when None =>
+ Internal_Detected := False;
+ end case;
+ Config.Valid_Port (Internal) := Internal_Detected;
+
+ -- PCH_HDMI_[BCD], PCH_DP_[BCD] share hotplug registers
+ for PCH_Port in PCH_HDMI_Port loop
+ Registers.Is_Set_Mask
+ (Register => PCH_HDMI (PCH_Port),
+ Mask => PCH_DIGI_PORT_DETECTED,
+ Result => HDMI_Detected);
+ Config.Valid_Port (To_Digital_Port (PCH_Port)) := HDMI_Detected;
+
+ Registers.Is_Set_Mask
+ (Register => PCH_DP (PCH_Port),
+ Mask => PCH_DIGI_PORT_DETECTED,
+ Result => DP_Detected);
+ Config.Valid_Port (To_DP_Port (PCH_Port)) := DP_Detected;
+
+ if HDMI_Detected or DP_Detected then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.SHOTPLUG_CTL,
+ Mask_Unset => SHOTPLUG_CTL_DETECT_MASK or
+ SHOTPLUG_CTL_SHORT_PULSE_MASK (PCH_Port),
+ Mask_Set => SHOTPLUG_CTL_HPD_INPUT_ENABLE (PCH_Port) or
+ SHOTPLUG_CTL_HPD_STATUS (PCH_Port)); -- clear
+ else
+ Registers.Unset_Mask
+ (Register => Registers.SHOTPLUG_CTL,
+ Mask => SHOTPLUG_CTL_DETECT_MASK or
+ SHOTPLUG_CTL_HPD_INPUT_ENABLE (PCH_Port));
+ end if;
+ end loop;
+ end Initialize;
+
+ procedure Hotplug_Detect (Port_Cfg : in Port_Config; Detected : out Boolean)
+ is
+ Ctl32 : Word32;
+ PCH_Port : constant GMA.PCH_Port :=
+ (case Port_Cfg.PCH_Port is
+ when PCH_DP_B => PCH_HDMI_B,
+ when PCH_DP_C => PCH_HDMI_C,
+ when PCH_DP_D => PCH_HDMI_D,
+ when others => Port_Cfg.PCH_Port);
+ begin
+ case PCH_Port is
+ when PCH_DAC =>
+ Registers.Read (Registers.PCH_ADPA, Ctl32, Verbose => False);
+ Ctl32 := Ctl32 and PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+ Detected := Ctl32 = PCH_ADPA_CRT_HPD_CHANNEL_MASK;
+ if Ctl32 /= 0 then
+ Registers.Set_Mask (Registers.PCH_ADPA, Ctl32);
+ end if;
+ when PCH_HDMI_B .. PCH_HDMI_D =>
+ Registers.Read (Registers.SHOTPLUG_CTL, Ctl32, Verbose => False);
+ Detected := (Ctl32 and SHOTPLUG_CTL_LONG_DETECT (PCH_Port)) /= 0;
+
+ if (Ctl32 and SHOTPLUG_CTL_HPD_STATUS (PCH_Port)) /= 0 then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.SHOTPLUG_CTL,
+ Mask_Unset => SHOTPLUG_CTL_DETECT_MASK,
+ Mask_Set => SHOTPLUG_CTL_HPD_STATUS (PCH_Port));
+ end if;
+ when others =>
+ Detected := False;
+ end case;
+ end Hotplug_Detect;
+
+end HW.GFX.GMA.Port_Detect;
diff --git a/common/ironlake/hw-gfx-gma-power_and_clocks.ads b/common/ironlake/hw-gfx-gma-power_and_clocks.ads
new file mode 100644
index 0000000..644c0c7
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-power_and_clocks.ads
@@ -0,0 +1,17 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Power_And_Clocks_Ironlake;
+
+private package HW.GFX.GMA.Power_And_Clocks
+ renames HW.GFX.GMA.Power_And_Clocks_Ironlake;
diff --git a/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.adb b/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.adb
new file mode 100644
index 0000000..e6fa3aa
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.adb
@@ -0,0 +1,54 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.Time;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.Power_And_Clocks_Ironlake is
+
+ PCH_DREF_CONTROL_120MHZ_CPU_OUTPUT_MASK : constant := 3 * 2 ** 13;
+ PCH_DREF_CONTROL_120MHZ_CPU_OUTPUT_SSC : constant := 2 * 2 ** 13;
+ PCH_DREF_CONTROL_120MHZ_CPU_OUTPUT_NONSSC : constant := 3 * 2 ** 13;
+ PCH_DREF_CONTROL_120MHZ_SSC_EN_MASK : constant := 3 * 2 ** 11;
+ PCH_DREF_CONTROL_120MHZ_SSC_EN : constant := 2 * 2 ** 11;
+ PCH_DREF_CONTROL_120MHZ_NONSSC_EN_MASK : constant := 3 * 2 ** 9;
+ PCH_DREF_CONTROL_120MHZ_NONSSC_EN : constant := 2 * 2 ** 9;
+ PCH_DREF_CONTROL_120MHZ_SSC4_EN_MASK : constant := 3 * 2 ** 7;
+ PCH_DREF_CONTROL_120MHZ_SSC4_EN : constant := 2 * 2 ** 7;
+ PCH_DREF_CONTROL_120MHZ_SSC4_DOWNSPREAD : constant := 0 * 2 ** 6;
+ PCH_DREF_CONTROL_120MHZ_SSC4_CENTERSPREAD : constant := 1 * 2 ** 6;
+ PCH_DREF_CONTROL_120MHZ_SSC_MODULATION_EN : constant := 1 * 2 ** 1;
+ PCH_DREF_CONTROL_120MHZ_SSC4_MODULATION_EN : constant := 1 * 2 ** 0;
+
+ procedure Initialize is
+ begin
+ -- ILK: enable non-spread spectrum clock, enable spread spectrum clock
+ Registers.Write
+ (Register => Registers.PCH_DREF_CONTROL,
+ Value => PCH_DREF_CONTROL_120MHZ_SSC_EN or
+ PCH_DREF_CONTROL_120MHZ_NONSSC_EN or
+ PCH_DREF_CONTROL_120MHZ_SSC_MODULATION_EN);
+ Registers.Posting_Read (Registers.PCH_DREF_CONTROL);
+ Time.U_Delay (1);
+ if Config.Internal_Is_EDP then -- TODO: check for presence
+ -- always use spread spectrum clock for CPU output
+ Registers.Set_Mask
+ (Register => Registers.PCH_DREF_CONTROL,
+ Mask => PCH_DREF_CONTROL_120MHZ_CPU_OUTPUT_SSC);
+ Registers.Posting_Read (Registers.PCH_DREF_CONTROL);
+ Time.U_Delay (20); -- DMI latency
+ end if;
+ end Initialize;
+
+end HW.GFX.GMA.Power_And_Clocks_Ironlake;
diff --git a/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.ads b/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.ads
new file mode 100644
index 0000000..f50388c
--- /dev/null
+++ b/common/ironlake/hw-gfx-gma-power_and_clocks_ironlake.ads
@@ -0,0 +1,29 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Power_And_Clocks_Ironlake is
+
+ procedure Initialize;
+
+ procedure Pre_All_Off is null;
+
+ procedure Post_All_Off is null;
+
+ procedure Power_Set_To (Configs : Configs_Type) is null;
+
+ procedure Power_Up (Old_Configs, New_Configs : Configs_Type) is null;
+
+ procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type)
+ is null;
+
+end HW.GFX.GMA.Power_And_Clocks_Ironlake;
diff --git a/common/skylake/Makefile.inc b/common/skylake/Makefile.inc
new file mode 100644
index 0000000..2dec2ef
--- /dev/null
+++ b/common/skylake/Makefile.inc
@@ -0,0 +1,10 @@
+gfxinit-y += hw-gfx-gma-plls-dpll.adb
+gfxinit-y += hw-gfx-gma-plls-dpll.ads
+gfxinit-y += hw-gfx-gma-plls-dpll_0.adb
+gfxinit-y += hw-gfx-gma-plls-dpll_0.ads
+gfxinit-y += hw-gfx-gma-plls.adb
+gfxinit-y += hw-gfx-gma-plls.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks.ads
+gfxinit-y += hw-gfx-gma-power_and_clocks_skylake.adb
+gfxinit-y += hw-gfx-gma-power_and_clocks_skylake.ads
+gfxinit-y += hw-gfx-gma-spll.ads
diff --git a/common/skylake/hw-gfx-gma-plls-dpll.adb b/common/skylake/hw-gfx-gma-plls-dpll.adb
new file mode 100644
index 0000000..c48f4ff
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls-dpll.adb
@@ -0,0 +1,357 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PLLs.DPLL is
+
+ -- NOTE: Order of DPLLs is twisted => always use named associations!
+
+ type Regs is array (Configurable_DPLLs) of Registers.Registers_Index;
+
+ DPLL_CTL : constant Regs := Regs'
+ (DPLL1 => Registers.LCPLL2_CTL,
+ DPLL2 => Registers.WRPLL_CTL_1,
+ DPLL3 => Registers.WRPLL_CTL_2);
+ DPLL_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
+
+ ----------------------------------------------------------------------------
+
+ DPLL_CFGR1 : constant Regs := Regs'
+ (DPLL1 => Registers.DPLL1_CFGR1,
+ DPLL2 => Registers.DPLL2_CFGR1,
+ DPLL3 => Registers.DPLL3_CFGR1);
+ DPLL_CFGR1_FREQUENCY_ENABLE : constant := 1 * 2 ** 31;
+ DPLL_CFGR1_DCO_FRACTION_SHIFT : constant := 9;
+ DPLL_CFGR1_DCO_FRACTION_MASK : constant := 16#7fff# * 2 ** 9;
+ DPLL_CFGR1_DCO_INTEGER_MASK : constant := 16#01ff# * 2 ** 0;
+
+ DPLL_CFGR2 : constant Regs := Regs'
+ (DPLL1 => Registers.DPLL1_CFGR2,
+ DPLL2 => Registers.DPLL2_CFGR2,
+ DPLL3 => Registers.DPLL3_CFGR2);
+ DPLL_CFGR2_QDIV_RATIO_SHIFT : constant := 8;
+ DPLL_CFGR2_QDIV_RATIO_MASK : constant := 255 * 2 ** 8;
+ DPLL_CFGR2_QDIV_MODE : constant := 1 * 2 ** 7;
+ DPLL_CFGR2_KDIV_SHIFT : constant := 5;
+ DPLL_CFGR2_KDIV_MASK : constant := 3 * 2 ** 5;
+ DPLL_CFGR2_PDIV_SHIFT : constant := 2;
+ DPLL_CFGR2_PDIV_MASK : constant := 7 * 2 ** 2;
+ DPLL_CFGR2_CENTRAL_FREQ_MASK : constant := 3 * 2 ** 0;
+ DPLL_CFGR2_CENTRAL_FREQ_9600MHZ : constant := 0 * 2 ** 0;
+ DPLL_CFGR2_CENTRAL_FREQ_9000MHZ : constant := 1 * 2 ** 0;
+ DPLL_CFGR2_CENTRAL_FREQ_8400MHZ : constant := 3 * 2 ** 0;
+
+ ----------------------------------------------------------------------------
+
+ HDMI_MODE : constant := 1 * 2 ** 5;
+ SSC : constant := 1 * 2 ** 4;
+ LINK_RATE_MASK : constant := 7 * 2 ** 1;
+ LINK_RATE_2700MHZ : constant := 0 * 2 ** 1;
+ LINK_RATE_1350MHZ : constant := 1 * 2 ** 1;
+ LINK_RATE_810MHZ : constant := 2 * 2 ** 1;
+ LINK_RATE_1620MHZ : constant := 3 * 2 ** 1;
+ LINK_RATE_1080MHZ : constant := 4 * 2 ** 1;
+ LINK_RATE_2160MHZ : constant := 5 * 2 ** 1;
+ OVERRIDE : constant := 1 * 2 ** 0;
+
+ LOCK : constant := 1 * 2 ** 0;
+
+ type Shifts is array (Configurable_DPLLs) of Natural;
+ DPLL_CTRL1_SHIFT : constant Shifts :=
+ (DPLL1 => 6, DPLL2 => 12, DPLL3 => 18);
+ DPLL_STATUS_SHIFT : constant Shifts :=
+ (DPLL1 => 8, DPLL2 => 16, DPLL3 => 24);
+
+ function LINK_RATE (Link_Rate : DP_Bandwidth) return Word32 is
+ begin
+ return (case Link_Rate is
+ when DP_Bandwidth_5_4 => LINK_RATE_2700MHZ,
+ when DP_Bandwidth_2_7 => LINK_RATE_1350MHZ,
+ when DP_Bandwidth_1_62 => LINK_RATE_810MHZ);
+ end LINK_RATE;
+
+ function DPLL_CTRL1_DPLLx
+ (Value : Word32;
+ PLL : Configurable_DPLLs)
+ return Word32 is
+ begin
+ return Shift_Left (Value, DPLL_CTRL1_SHIFT (PLL));
+ end DPLL_CTRL1_DPLLx;
+
+ function DPLL_STATUS_DPLLx_LOCK (PLL : Configurable_DPLLs) return Word32 is
+ begin
+ return Shift_Left (LOCK, DPLL_STATUS_SHIFT (PLL));
+ end DPLL_STATUS_DPLLx_LOCK;
+
+ ----------------------------------------------------------------------------
+
+ subtype PDiv_Range is Pos64 range 1 .. 7;
+ subtype QDiv_Range is Pos64 range 1 .. 255;
+ subtype KDiv_Range is Pos64 range 1 .. 5;
+
+ type Central_Frequency is (CF_INVALID, CF_9600MHZ, CF_9000MHZ, CF_8400MHZ);
+ subtype Valid_Central_Freq is
+ Central_Frequency range CF_9600MHZ .. CF_8400MHZ;
+
+ type CF_Pos is array (Valid_Central_Freq) of Pos64;
+ CF_Pos64 : constant CF_Pos := CF_Pos'
+ (CF_9600MHZ => 9_600_000_000,
+ CF_9000MHZ => 9_000_000_000,
+ CF_8400MHZ => 8_400_000_000);
+
+ subtype DCO_Frequency is
+ Pos64 range 1 .. CF_Pos64 (CF_9600MHZ) + CF_Pos64 (CF_9600MHZ) / 100;
+
+ function DPLL_CFGR1_DCO_FRACTION (DCO_Freq : DCO_Frequency) return Word32
+ with
+ Pre => True
+ is
+ begin
+ return Shift_Left
+ (Word32 ((DCO_Freq * 2 ** 15) / 24_000_000) and 16#7fff#,
+ DPLL_CFGR1_DCO_FRACTION_SHIFT);
+ end DPLL_CFGR1_DCO_FRACTION;
+
+ function DPLL_CFGR1_DCO_INTEGER (DCO_Freq : DCO_Frequency) return Word32
+ with
+ Pre => True
+ is
+ begin
+ return Word32 (DCO_Freq / 24_000_000);
+ end DPLL_CFGR1_DCO_INTEGER;
+
+ function DPLL_CFGR2_PDIV (PDiv : PDiv_Range) return Word32 is
+ begin
+ return Shift_Left
+ ((case PDiv is
+ when 1 => 0,
+ when 2 => 1,
+ when 3 => 2,
+ when 7 => 4,
+ when others => 4),
+ DPLL_CFGR2_PDIV_SHIFT);
+ end DPLL_CFGR2_PDIV;
+
+ function DPLL_CFGR2_QDIV (QDiv : QDiv_Range) return Word32 is
+ begin
+ return Shift_Left (Word32 (QDiv), DPLL_CFGR2_QDIV_RATIO_SHIFT) or
+ (if QDiv /= 1 then DPLL_CFGR2_QDIV_MODE else 0);
+ end DPLL_CFGR2_QDIV;
+
+ function DPLL_CFGR2_KDIV (KDiv : KDiv_Range) return Word32 is
+ begin
+ return Shift_Left
+ ((case KDiv is
+ when 5 => 0,
+ when 2 => 1,
+ when 3 => 2,
+ when 1 => 3,
+ when others => 0),
+ DPLL_CFGR2_KDIV_SHIFT);
+ end DPLL_CFGR2_KDIV;
+
+ function DPLL_CFGR2_CENTRAL_FREQ (CF : Valid_Central_Freq) return Word32 is
+ begin
+ return (case CF is
+ when CF_9600MHZ => DPLL_CFGR2_CENTRAL_FREQ_9600MHZ,
+ when CF_9000MHZ => DPLL_CFGR2_CENTRAL_FREQ_9000MHZ,
+ when CF_8400MHZ => DPLL_CFGR2_CENTRAL_FREQ_8400MHZ);
+ end DPLL_CFGR2_CENTRAL_FREQ;
+
+ ----------------------------------------------------------------------------
+
+ procedure Calculate_DPLL
+ (Dotclock : in Frequency_Type;
+ Central_Freq : out Central_Frequency;
+ DCO_Freq : out DCO_Frequency;
+ PDiv : out PDiv_Range;
+ QDiv : out QDiv_Range;
+ KDiv : out KDiv_Range)
+ with
+ Pre => True
+ is
+ Max_Pos_Deviation : constant := 1;
+ Max_Neg_Deviation : constant := 6;
+
+ subtype Div_Range is Pos64 range 1 .. 98;
+ subtype Candidate_Index is Positive range 1 .. 36;
+ type Candidate_Array is array (Candidate_Index) of Div_Range;
+ type Candidate_List is record
+ Divs : Candidate_Array;
+ Count : Candidate_Index;
+ end record;
+ type Parity_Type is (Even, Odd);
+ type Candidates_Type is array (Parity_Type) of Candidate_List;
+
+ Candidates : constant Candidates_Type := Candidates_Type'
+ (Even => Candidate_List'
+ (Divs => Candidate_Array'
+ (4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44,
+ 48, 52, 54, 56, 60, 64, 66, 68, 70, 72, 76, 78, 80, 84, 88, 90,
+ 92, 96, 98),
+ Count => 36),
+ Odd => Candidate_List'
+ (Divs => Candidate_Array'(3, 5, 7, 9, 15, 21, 35, others => 1),
+ Count => 7));
+
+ Temp_Freq,
+ Allowed_Deviation : Pos64;
+ Deviation : Int64;
+ Temp_Central : DCO_Frequency;
+ Min_Deviation : Int64 := Int64'Last;
+ Div : Div_Range := Div_Range'Last;
+ begin
+ Central_Freq := CF_INVALID;
+ DCO_Freq := 1;
+ PDiv := 1;
+ QDiv := 1;
+ KDiv := 1;
+
+ for Parity in Parity_Type loop
+ for CF in Valid_Central_Freq loop
+ Temp_Central := CF_Pos64 (CF);
+ for I in Candidate_Index range 1 .. Candidates (Parity).Count loop
+ Temp_Freq := Candidates (Parity).Divs (I) * 5 * Dotclock;
+ if Temp_Freq > Temp_Central then
+ Deviation := Temp_Freq - Temp_Central;
+ Allowed_Deviation := (Max_Pos_Deviation * Temp_Central) / 100;
+ else
+ Deviation := Temp_Central - Temp_Freq;
+ Allowed_Deviation := (Max_Neg_Deviation * Temp_Central) / 100;
+ end if;
+ if Deviation < Min_Deviation and
+ Deviation < Allowed_Deviation
+ then
+ Min_Deviation := Deviation;
+ Central_Freq := CF;
+ DCO_Freq := Temp_Freq;
+ Div := Candidates (Parity).Divs (I);
+ end if;
+ end loop;
+ end loop;
+ exit when Central_Freq /= CF_INVALID;
+ end loop;
+
+ if Central_Freq /= CF_INVALID then
+ if Div mod 2 = 0 then
+ pragma Assert (Div /= 1);
+ pragma Assert (Div > 1);
+ Div := Div / 2;
+ if Div = 1 or Div = 3 or Div = 5 then
+ -- 2, 6 and 10
+ PDiv := 2;
+ QDiv := 1;
+ KDiv := Div;
+ elsif Div mod 2 = 0 then
+ -- divisible by 4
+ PDiv := 2;
+ QDiv := Div / 2;
+ KDiv := 2;
+ elsif Div mod 3 = 0 then
+ -- divisible by 6
+ PDiv := 3;
+ QDiv := Div / 3;
+ KDiv := 2;
+ elsif Div mod 7 = 0 then
+ -- divisible by 14
+ PDiv := 7;
+ QDiv := Div / 7;
+ KDiv := 2;
+ end if;
+ elsif Div = 7 or Div = 21 or Div = 35 then
+ -- 7, 21 and 35
+ PDiv := 7;
+ QDiv := 1;
+ KDiv := Div / 7;
+ elsif Div = 3 or Div = 9 or Div = 15 then
+ -- 3, 9 and 15
+ PDiv := 3;
+ QDiv := 1;
+ KDiv := Div / 3;
+ elsif Div = 5 then
+ -- 5
+ PDiv := 5;
+ QDiv := 1;
+ KDiv := 1;
+ end if;
+ end if;
+ end Calculate_DPLL;
+
+ ----------------------------------------------------------------------------
+
+ procedure On
+ (PLL : in Configurable_DPLLs;
+ Port_Cfg : in Port_Config;
+ Success : out Boolean)
+ is
+ Central_Freq : Central_Frequency;
+ DCO_Freq : DCO_Frequency;
+ PDiv : PDiv_Range;
+ QDiv : QDiv_Range;
+ KDiv : KDiv_Range;
+ begin
+ if Port_Cfg.Display = DP then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.DPLL_CTRL1,
+ Mask_Unset => DPLL_CTRL1_DPLLx (HDMI_MODE, PLL) or
+ DPLL_CTRL1_DPLLx (SSC, PLL) or
+ DPLL_CTRL1_DPLLx (LINK_RATE_MASK, PLL),
+ Mask_Set => DPLL_CTRL1_DPLLx (LINK_RATE
+ (Port_Cfg.DP.Bandwidth), PLL) or
+ DPLL_CTRL1_DPLLx (OVERRIDE, PLL));
+ Registers.Posting_Read (Registers.DPLL_CTRL1);
+ Success := True;
+ else
+ Calculate_DPLL
+ (Port_Cfg.Mode.Dotclock, Central_Freq, DCO_Freq, PDiv, QDiv, KDiv);
+ Success := Central_Freq /= CF_INVALID;
+ if Success then
+ Registers.Unset_And_Set_Mask
+ (Register => Registers.DPLL_CTRL1,
+ Mask_Unset => DPLL_CTRL1_DPLLx (SSC, PLL),
+ Mask_Set => DPLL_CTRL1_DPLLx (HDMI_MODE, PLL) or
+ DPLL_CTRL1_DPLLx (OVERRIDE, PLL));
+ Registers.Write
+ (Register => DPLL_CFGR1 (PLL),
+ Value => DPLL_CFGR1_FREQUENCY_ENABLE or
+ DPLL_CFGR1_DCO_FRACTION (DCO_Freq) or
+ DPLL_CFGR1_DCO_INTEGER (DCO_Freq));
+ Registers.Write
+ (Register => DPLL_CFGR2 (PLL),
+ Value => DPLL_CFGR2_QDIV (QDiv) or
+ DPLL_CFGR2_KDIV (KDiv) or
+ DPLL_CFGR2_PDIV (PDiv) or
+ DPLL_CFGR2_CENTRAL_FREQ (Central_Freq));
+ Registers.Posting_Read (Registers.DPLL_CTRL1);
+ Registers.Posting_Read (DPLL_CFGR1 (PLL));
+ Registers.Posting_Read (DPLL_CFGR2 (PLL));
+ end if;
+ end if;
+
+ if Success then
+ Registers.Write
+ (Register => DPLL_CTL (PLL),
+ Value => DPLL_CTL_PLL_ENABLE);
+ Registers.Wait_Set_Mask
+ (Register => Registers.DPLL_STATUS,
+ Mask => DPLL_STATUS_DPLLx_LOCK (PLL));
+ end if;
+ end On;
+
+ procedure Off (PLL : Configurable_DPLLs) is
+ begin
+ Registers.Unset_Mask (DPLL_CTL (PLL), DPLL_CTL_PLL_ENABLE);
+ end Off;
+
+end HW.GFX.GMA.PLLs.DPLL;
diff --git a/common/skylake/hw-gfx-gma-plls-dpll.ads b/common/skylake/hw-gfx-gma-plls-dpll.ads
new file mode 100644
index 0000000..61bd55e
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls-dpll.ads
@@ -0,0 +1,27 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs.DPLL is
+
+ type Value_Array is array (Configurable_DPLLs) of Word32;
+ Register_Value : constant Value_Array := Value_Array'
+ (DPLL1 => 1, DPLL2 => 2, DPLL3 => 3);
+
+ procedure On
+ (PLL : in Configurable_DPLLs;
+ Port_Cfg : in Port_Config;
+ Success : out Boolean);
+
+ procedure Off (PLL : Configurable_DPLLs);
+
+end HW.GFX.GMA.PLLs.DPLL;
diff --git a/common/skylake/hw-gfx-gma-plls-dpll_0.adb b/common/skylake/hw-gfx-gma-plls-dpll_0.adb
new file mode 100644
index 0000000..f981940
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls-dpll_0.adb
@@ -0,0 +1,48 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Registers;
+
+package body HW.GFX.GMA.PLLs.DPLL_0 is
+
+ DPLL_CTRL1_DPLL0_LINK_RATE_MASK : constant := 7 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ : constant := 0 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ : constant := 1 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ : constant := 2 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_1620MHZ : constant := 3 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_1080MHZ : constant := 4 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_2160MHZ : constant := 5 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_OVERRIDE : constant := 1 * 2 ** 0;
+
+ procedure Check_Link_Rate
+ (Link_Rate : in DP_Bandwidth;
+ Success : out Boolean)
+ is
+ DPLL_Ctrl1 : Word32;
+ begin
+ Registers.Read (Registers.DPLL_CTRL1, DPLL_Ctrl1);
+
+ case DPLL_Ctrl1 and DPLL_CTRL1_DPLL0_LINK_RATE_MASK is
+ when DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ =>
+ Success := Link_Rate = DP_Bandwidth_5_4;
+ when DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ =>
+ Success := Link_Rate = DP_Bandwidth_2_7;
+ when DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ =>
+ Success := Link_Rate = DP_Bandwidth_1_62;
+ when others =>
+ Success := False;
+ end case;
+ Success := Success and (DPLL_Ctrl1 and DPLL_CTRL1_DPLL0_OVERRIDE) /= 0;
+ end Check_Link_Rate;
+
+end HW.GFX.GMA.PLLs.DPLL_0;
diff --git a/common/skylake/hw-gfx-gma-plls-dpll_0.ads b/common/skylake/hw-gfx-gma-plls-dpll_0.ads
new file mode 100644
index 0000000..a2fecfa
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls-dpll_0.ads
@@ -0,0 +1,22 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs.DPLL_0 is
+
+ Register_Value : constant Word32 := 0;
+
+ procedure Check_Link_Rate
+ (Link_Rate : in DP_Bandwidth;
+ Success : out Boolean);
+
+end HW.GFX.GMA.PLLs.DPLL_0;
diff --git a/common/skylake/hw-gfx-gma-plls.adb b/common/skylake/hw-gfx-gma-plls.adb
new file mode 100644
index 0000000..561f8ad
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls.adb
@@ -0,0 +1,151 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.PLLs.DPLL_0;
+with HW.GFX.GMA.PLLs.DPLL;
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+package body HW.GFX.GMA.PLLs
+with
+ Refined_State => (State => PLLs)
+is
+
+ type Count_Range is new Natural range 0 .. 2;
+
+ type PLL_State is record
+ Use_Count : Count_Range;
+ Used_For_DP : Boolean;
+ Link_Rate : DP_Bandwidth;
+ Mode : Mode_Type;
+ end record;
+
+ type PLL_State_Array is array (Configurable_DPLLs) of PLL_State;
+
+ PLLs : PLL_State_Array;
+
+ procedure Initialize
+ is
+ begin
+ PLLs :=
+ (Configurable_DPLLs =>
+ (Use_Count => 0,
+ Used_For_DP => False,
+ Link_Rate => DP_Bandwidth'First,
+ Mode => Invalid_Mode));
+ end Initialize;
+
+ procedure Alloc_Configurable
+ (Port_Cfg : in Port_Config;
+ PLL : out T;
+ Success : out Boolean)
+ with
+ Pre => True
+ is
+ function Config_Matches (PE : PLL_State) return Boolean
+ is
+ begin
+ return
+ PE.Used_For_DP = (Port_Cfg.Display = DP) and
+ ((PE.Used_For_DP and PE.Link_Rate = Port_Cfg.DP.Bandwidth) or
+ (not PE.Used_For_DP and PE.Mode = Port_Cfg.Mode));
+ end Config_Matches;
+ begin
+ -- try to find shareable PLL
+ for P in Configurable_DPLLs loop
+ Success := PLLs (P).Use_Count /= 0 and
+ PLLs (P).Use_Count /= Count_Range'Last and
+ Config_Matches (PLLs (P));
+ if Success then
+ PLL := P;
+ PLLs (PLL).Use_Count := PLLs (PLL).Use_Count + 1;
+ return;
+ end if;
+ end loop;
+
+ -- try to find free PLL
+ for P in Configurable_DPLLs loop
+ if PLLs (P).Use_Count = 0 then
+ PLL := P;
+ DPLL.On (PLL, Port_Cfg, Success);
+ if Success then
+ PLLs (PLL) :=
+ (Use_Count => 1,
+ Used_For_DP => Port_Cfg.Display = DP,
+ Link_Rate => Port_Cfg.DP.Bandwidth,
+ Mode => Port_Cfg.Mode);
+ end if;
+ return;
+ end if;
+ end loop;
+
+ PLL := Invalid;
+ end Alloc_Configurable;
+
+ procedure Alloc
+ (Port_Cfg : in Port_Config;
+ PLL : out T;
+ Success : out Boolean)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if Port_Cfg.Port = DIGI_A then
+ DPLL_0.Check_Link_Rate (Port_Cfg.DP.Bandwidth, Success);
+ else
+ Success := False;
+ end if;
+
+ if Success then
+ PLL := DPLL0;
+ else
+ Alloc_Configurable (Port_Cfg, PLL, Success);
+ end if;
+ end Alloc;
+
+ procedure Free (PLL : T)
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ if PLL in Configurable_DPLLs then
+ if PLLs (PLL).Use_Count /= 0 then
+ PLLs (PLL).Use_Count := PLLs (PLL).Use_Count - 1;
+ if PLLs (PLL).Use_Count = 0 then
+ DPLL.Off (PLL);
+ end if;
+ end if;
+ end if;
+ end Free;
+
+ procedure All_Off
+ is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ for PLL in Configurable_DPLLs loop
+ DPLL.Off (PLL);
+ end loop;
+ end All_Off;
+
+ function Register_Value (PLL : T) return Word32
+ is
+ begin
+ return
+ (if PLL = DPLL0 then DPLL_0.Register_Value
+ elsif PLL in Configurable_DPLLs then DPLL.Register_Value (PLL)
+ else 0);
+ end Register_Value;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/skylake/hw-gfx-gma-plls.ads b/common/skylake/hw-gfx-gma-plls.ads
new file mode 100644
index 0000000..9407af0
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-plls.ads
@@ -0,0 +1,42 @@
+--
+-- Copyright (C) 2015-2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.PLLs
+with
+ Abstract_State => (State with Part_Of => GMA.State)
+is
+
+ -- NOTE: Order of DPLLs is twisted, as DPLL2 (WRPLL1)
+ -- should be selected as last choice.
+
+ -- XXX: Types should be private (but that triggers a bug in SPARK GPL 2016)
+ type T is (Invalid_PLL, DPLL0, DPLL1, DPLL3, DPLL2);
+ subtype Configurable_DPLLs is T range DPLL1 .. DPLL2;
+ Invalid : constant T := Invalid_PLL;
+
+ procedure Initialize
+ with
+ Global => (Output => State);
+
+ procedure Alloc
+ (Port_Cfg : in Port_Config;
+ PLL : out T;
+ Success : out Boolean);
+
+ procedure Free (PLL : T);
+
+ procedure All_Off;
+
+ function Register_Value (PLL : T) return Word32;
+
+end HW.GFX.GMA.PLLs;
diff --git a/common/skylake/hw-gfx-gma-power_and_clocks.ads b/common/skylake/hw-gfx-gma-power_and_clocks.ads
new file mode 100644
index 0000000..bf54989
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-power_and_clocks.ads
@@ -0,0 +1,17 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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 HW.GFX.GMA.Power_And_Clocks_Skylake;
+
+private package HW.GFX.GMA.Power_And_Clocks
+ renames HW.GFX.GMA.Power_And_Clocks_Skylake;
diff --git a/common/skylake/hw-gfx-gma-power_and_clocks_skylake.adb b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.adb
new file mode 100644
index 0000000..521ef8b
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.adb
@@ -0,0 +1,351 @@
+--
+-- Copyright (C) 2014-2016 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; version 2 of the License.
+--
+-- 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 GNAT.Source_Info;
+
+with HW.Time;
+with HW.Debug;
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.Registers;
+with HW.GFX.GMA.Power_And_Clocks_Haswell;
+
+use type HW.Word64;
+
+package body HW.GFX.GMA.Power_And_Clocks_Skylake is
+
+ type Power_Domain is (MISC_IO, PW1, PW2, DDI_AE, DDI_B, DDI_C, DDI_D);
+ subtype Power_Well is Power_Domain range PW1 .. PW2;
+ subtype Dynamic_Domain is Power_Domain range PW2 .. DDI_D;
+
+ NDE_RSTWRN_OPT_RST_PCH_Handshake_En : constant := 1 * 2 ** 4;
+
+ FUSE_STATUS_DOWNLOAD_STATUS : constant := 1 * 2 ** 31;
+ FUSE_STATUS_PG0_DIST_STATUS : constant := 1 * 2 ** 27;
+
+ type Power_Domain_Values is array (Power_Domain) of Word32;
+ PWR_WELL_CTL_POWER_REQUEST : constant Power_Domain_Values :=
+ (MISC_IO => 1 * 2 ** 1,
+ DDI_AE => 1 * 2 ** 3,
+ DDI_B => 1 * 2 ** 5,
+ DDI_C => 1 * 2 ** 7,
+ DDI_D => 1 * 2 ** 9,
+ PW1 => 1 * 2 ** 29,
+ PW2 => 1 * 2 ** 31);
+ PWR_WELL_CTL_POWER_STATE : constant Power_Domain_Values :=
+ (MISC_IO => 1 * 2 ** 0,
+ DDI_AE => 1 * 2 ** 2,
+ DDI_B => 1 * 2 ** 4,
+ DDI_C => 1 * 2 ** 6,
+ DDI_D => 1 * 2 ** 8,
+ PW1 => 1 * 2 ** 28,
+ PW2 => 1 * 2 ** 30);
+
+ type Power_Well_Values is array (Power_Well) of Word32;
+ FUSE_STATUS_PGx_DIST_STATUS : constant Power_Well_Values :=
+ (PW1 => 1 * 2 ** 26,
+ PW2 => 1 * 2 ** 25);
+
+ DBUF_CTL_DBUF_POWER_REQUEST : constant := 1 * 2 ** 31;
+ DBUF_CTL_DBUF_POWER_STATE : constant := 1 * 2 ** 30;
+
+ ----------------------------------------------------------------------------
+
+ DPLL_CTRL1_DPLL0_LINK_RATE_MASK : constant := 7 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_2700MHZ : constant := 0 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_1350MHZ : constant := 1 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_810MHZ : constant := 2 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_1620MHZ : constant := 3 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_1080MHZ : constant := 4 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_LINK_RATE_2160MHZ : constant := 5 * 2 ** 1;
+ DPLL_CTRL1_DPLL0_OVERRIDE : constant := 1 * 2 ** 0;
+
+ LCPLL1_CTL_PLL_ENABLE : constant := 1 * 2 ** 31;
+ LCPLL1_CTL_PLL_LOCK : constant := 1 * 2 ** 30;
+
+ ----------------------------------------------------------------------------
+
+ CDCLK_CTL_CD_FREQ_SELECT_MASK : constant := 3 * 2 ** 26;
+ CDCLK_CTL_CD_FREQ_SELECT_450MHZ : constant := 0 * 2 ** 26;
+ CDCLK_CTL_CD_FREQ_SELECT_540MHZ : constant := 1 * 2 ** 26;
+ CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ : constant := 2 * 2 ** 26;
+ CDCLK_CTL_CD_FREQ_SELECT_675MHZ : constant := 3 * 2 ** 26;
+ CDCLK_CTL_CD_FREQ_DECIMAL_MASK : constant := 16#7ff#;
+
+ SKL_PCODE_CDCLK_CONTROL : constant := 7;
+ SKL_CDCLK_PREPARE_FOR_CHANGE : constant := 3;
+ SKL_CDCLK_READY_FOR_CHANGE : constant := 1;
+
+ GT_MAILBOX_READY : constant := 1 * 2 ** 31;
+
+ function CDCLK_CTL_CD_FREQ_DECIMAL
+ (Freq : Positive;
+ Plus_Half : Boolean)
+ return Word32 is
+ begin
+ return Word32 (2 * (Freq - 1)) or (if Plus_Half then 1 else 0);
+ end CDCLK_CTL_CD_FREQ_DECIMAL;
+
+ ----------------------------------------------------------------------------
+
+ procedure GT_Mailbox_Write (MBox : Word32; Value : Word64) is
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+ Registers.Write
+ (Registers.GT_MAILBOX_DATA, Word32 (Value and 16#ffff_ffff#));
+ Registers.Write
+ (Registers.GT_MAILBOX_DATA_1, Word32 (Shift_Right (Value, 32)));
+ Registers.Write (Registers.GT_MAILBOX, GT_MAILBOX_READY or MBox);
+
+ Registers.Wait_Unset_Mask (Registers.GT_MAILBOX, GT_MAILBOX_READY);
+ end GT_Mailbox_Write;
+
+ ----------------------------------------------------------------------------
+
+ procedure PD_Off (PD : Power_Domain)
+ is
+ Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+ Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+ Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+ Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
+
+ if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+ PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0
+ then
+ Registers.Wait_Set_Mask
+ (Register => Registers.PWR_WELL_CTL_DRIVER,
+ Mask => PWR_WELL_CTL_POWER_STATE (PD));
+ end if;
+
+ if (Ctl1 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
+ Registers.Unset_Mask
+ (Register => Registers.PWR_WELL_CTL_BIOS,
+ Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
+ end if;
+
+ if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) /= 0 then
+ Registers.Unset_Mask
+ (Register => Registers.PWR_WELL_CTL_DRIVER,
+ Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
+ end if;
+ end PD_Off;
+
+ procedure PD_On (PD : Power_Domain)
+ with
+ Pre => True
+ is
+ Ctl1, Ctl2, Ctl3, Ctl4 : Word32;
+ begin
+ pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+ Registers.Read (Registers.PWR_WELL_CTL_BIOS, Ctl1);
+ Registers.Read (Registers.PWR_WELL_CTL_DRIVER, Ctl2);
+ Registers.Read (Registers.PWR_WELL_CTL_KVMR, Ctl3);
+ Registers.Read (Registers.PWR_WELL_CTL_DEBUG, Ctl4);
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL5)); -- Result for debugging only
+ pragma Debug (Registers.Posting_Read (Registers.PWR_WELL_CTL6)); -- Result for debugging only
+
+ if ((Ctl1 or Ctl2 or Ctl3 or Ctl4) and
+ PWR_WELL_CTL_POWER_REQUEST (PD)) = 0
+ then
+ Registers.Wait_Unset_Mask
+ (Register => Registers.PWR_WELL_CTL_DRIVER,
+ Mask => PWR_WELL_CTL_POWER_STATE (PD));
+ end if;
+
+ if (Ctl2 and PWR_WELL_CTL_POWER_REQUEST (PD)) = 0 then
+ Registers.Set_Mask
+ (Register => Registers.PWR_WELL_CTL_DRIVER,
+ Mask => PWR_WELL_CTL_POWER_REQUEST (PD));
+ Registers.Wait_Set_Mask
+ (Register => Registers.PWR_WELL_CTL_DRIVER,
+ Mask => PWR_WELL_CTL_POWER_STATE (PD));
+
+ if PD in Power_Well then
+ Registers.Wait_Set_Mask
+ (Register => Registers.FUSE_STATUS,
+ Mask => FUSE_STATUS_PGx_DIST_STATUS (PD));
+ end if;
+ end if;
+ end PD_On;
+
+ function Need_PD (PD : Dynamic_Domain; Configs : Configs_Type) return Boolean
+ is
+ begin
+ return (case PD is
+ when DDI_AE => Configs (Primary).Port = Internal or
+ Configs (Secondary).Port = Internal or
+ Configs (Tertiary).Port = Internal,
+ when DDI_B => Configs (Primary).Port = Digital1 or
+ Configs (Primary).Port = DP1 or
+ Configs (Secondary).Port = Digital1 or
+ Configs (Secondary).Port = DP1 or
+ Configs (Tertiary).Port = Digital1 or
+ Configs (Tertiary).Port = DP1,
+ when DDI_C => Configs (Primary).Port = Digital2 or
+ Configs (Primary).Port = DP2 or
+ Configs (Secondary).Port = Digital2 or
+ Configs (Secondary).Port = DP2 or
+ Configs (Tertiary).Port = Digital2 or
+ Configs (Tertiary).Port = DP2,
+ when DDI_D => Configs (Primary).Port = Digital3 or
+ Configs (Primary).Port = DP3 or
+ Configs (Secondary).Port = Digital3 or
+ Configs (Secondary).Port = DP3 or
+ Configs (Tertiary).Port = Digital3 or
+ Configs (Tertiary).Port = DP3,
+ when PW2 => (Configs (Primary).Port /= Disabled and
+ Configs (Primary).Port /= Internal) or
+ Configs (Secondary).Port /= Disabled or
+ Configs (Tertiary).Port /= Disabled);
+ end Need_PD;
+
+ ----------------------------------------------------------------------------
+
+ procedure Pre_All_Off is
+ begin
+ Power_And_Clocks_Haswell.PSR_Off;
+ end Pre_All_Off;
+
+ procedure Post_All_Off is
+ begin
+ for PD in reverse Dynamic_Domain loop
+ PD_Off (PD);
+ end loop;
+
+ Registers.Unset_Mask
+ (Register => Registers.DBUF_CTL,
+ Mask => DBUF_CTL_DBUF_POWER_REQUEST);
+ Registers.Wait_Unset_Mask
+ (Register => Registers.DBUF_CTL,
+ Mask => DBUF_CTL_DBUF_POWER_STATE);
+
+ Registers.Unset_Mask
+ (Register => Registers.LCPLL1_CTL,
+ Mask => LCPLL1_CTL_PLL_ENABLE);
+ Registers.Wait_Unset_Mask
+ (Register => Registers.LCPLL1_CTL,
+ Mask => LCPLL1_CTL_PLL_LOCK);
+
+ PD_Off (MISC_IO);
+ PD_Off (PW1);
+ end Post_All_Off;
+
+ procedure Initialize
+ is
+ CDClk_Change_Timeout : Time.T;
+ Timed_Out : Boolean;
+
+ MBox_Data0 : Word32;
+ begin
+ Registers.Set_Mask
+ (Register => Registers.NDE_RSTWRN_OPT,
+ Mask => NDE_RSTWRN_OPT_RST_PCH_Handshake_En);
+
+ Registers.Wait_Set_Mask
+ (Register => Registers.FUSE_STATUS,
+ Mask => FUSE_STATUS_PG0_DIST_STATUS);
+ PD_On (PW1);
+ PD_On (MISC_IO);
+
+ Registers.Write
+ (Register => Registers.CDCLK_CTL,
+ Value => CDCLK_CTL_CD_FREQ_SELECT_337_5MHZ or
+ CDCLK_CTL_CD_FREQ_DECIMAL (337, True));
+ -- TODO: Set to preferred eDP rate:
+ -- Registers.Unset_And_Set_Mask
+ -- (Register => Registers.DPLL_CTRL1,
+ -- Unset_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_MASK,
+ -- Set_Mask => DPLL_CTRL1_DPLL0_LINK_RATE_...);
+ Registers.Set_Mask
+ (Register => Registers.LCPLL1_CTL,
+ Mask => LCPLL1_CTL_PLL_ENABLE);
+ Registers.Wait_Set_Mask
+ (Register => Registers.LCPLL1_CTL,
+ Mask => LCPLL1_CTL_PLL_LOCK);
+
+ CDClk_Change_Timeout := Time.MS_From_Now (3);
+ loop
+ GT_Mailbox_Write
+ (MBox => SKL_PCODE_CDCLK_CONTROL,
+ Value => SKL_CDCLK_PREPARE_FOR_CHANGE);
+ Timed_Out := Time.Timed_Out (CDClk_Change_Timeout);
+ Registers.Read (Registers.GT_MAILBOX_DATA, MBox_Data0);
+ if (MBox_Data0 and SKL_CDCLK_READY_FOR_CHANGE) =
+ SKL_CDCLK_READY_FOR_CHANGE
+ then
+ Timed_Out := False;
+ exit;
+ end if;
+ exit when Timed_Out;
+ end loop;
+
+ if not Timed_Out then
+ GT_Mailbox_Write
+ (MBox => SKL_PCODE_CDCLK_CONTROL,
+ Value => 16#0000_0000#); -- 0 - 337.5MHz
+ -- 1 - 450.0MHz
+ -- 2 - 540.0MHz
+ -- 3 - 675.0MHz
+ Registers.Set_Mask
+ (Register => Registers.DBUF_CTL,
+ Mask => DBUF_CTL_DBUF_POWER_REQUEST);
+ Registers.Wait_Set_Mask
+ (Register => Registers.DBUF_CTL,
+ Mask => DBUF_CTL_DBUF_POWER_STATE);
+ end if;
+ end Initialize;
+
+ procedure Power_Set_To (Configs : Configs_Type) is
+ begin
+ for PD in reverse Dynamic_Domain loop
+ if not Need_PD (PD, Configs) then
+ PD_Off (PD);
+ end if;
+ end loop;
+ for PD in Dynamic_Domain loop
+ if Need_PD (PD, Configs) then
+ PD_On (PD);
+ end if;
+ end loop;
+ end Power_Set_To;
+
+ procedure Power_Up (Old_Configs, New_Configs : Configs_Type) is
+ begin
+ for PD in Dynamic_Domain loop
+ if not Need_PD (PD, Old_Configs) and Need_PD (PD, New_Configs) then
+ PD_On (PD);
+ end if;
+ end loop;
+ end Power_Up;
+
+ procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type)
+ is
+ begin
+ for PD in reverse Dynamic_Domain loop
+ if (Need_PD (PD, Old_Configs) or Need_PD (PD, Tmp_Configs)) and
+ not Need_PD (PD, New_Configs)
+ then
+ PD_Off (PD);
+ end if;
+ end loop;
+ end Power_Down;
+
+end HW.GFX.GMA.Power_And_Clocks_Skylake;
diff --git a/common/skylake/hw-gfx-gma-power_and_clocks_skylake.ads b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.ads
new file mode 100644
index 0000000..017ca65
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-power_and_clocks_skylake.ads
@@ -0,0 +1,25 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+private package HW.GFX.GMA.Power_And_Clocks_Skylake is
+
+ procedure Pre_All_Off;
+ procedure Post_All_Off;
+
+ procedure Initialize;
+
+ procedure Power_Set_To (Configs : Configs_Type);
+ procedure Power_Up (Old_Configs, New_Configs : Configs_Type);
+ procedure Power_Down (Old_Configs, Tmp_Configs, New_Configs : Configs_Type);
+
+end HW.GFX.GMA.Power_And_Clocks_Skylake;
diff --git a/common/skylake/hw-gfx-gma-spll.ads b/common/skylake/hw-gfx-gma-spll.ads
new file mode 100644
index 0000000..d9af288
--- /dev/null
+++ b/common/skylake/hw-gfx-gma-spll.ads
@@ -0,0 +1,23 @@
+--
+-- Copyright (C) 2016 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; version 2 of the License.
+--
+-- 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.
+--
+
+package HW.GFX.GMA.SPLL is
+
+ -- Just for a common interface with Haswell's DDI.
+ -- There is no SPLL (no FDI) on Skylake.
+
+ procedure On is null;
+
+ procedure Off is null;
+
+end HW.GFX.GMA.SPLL;
diff --git a/configs/broadwell b/configs/broadwell
new file mode 100644
index 0000000..51b9df7
--- /dev/null
+++ b/configs/broadwell
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Broadwell
+CONFIG_GFX_GMA_CPU_VARIANT = Normal
+CONFIG_GFX_GMA_INTERNAL_PORT = DP
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#
diff --git a/configs/broadwell_ult b/configs/broadwell_ult
new file mode 100644
index 0000000..f209293
--- /dev/null
+++ b/configs/broadwell_ult
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Broadwell
+CONFIG_GFX_GMA_CPU_VARIANT = ULT
+CONFIG_GFX_GMA_INTERNAL_PORT = DP
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#
diff --git a/configs/haswell b/configs/haswell
new file mode 100644
index 0000000..724d651
--- /dev/null
+++ b/configs/haswell
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Haswell
+CONFIG_GFX_GMA_CPU_VARIANT = Normal
+CONFIG_GFX_GMA_INTERNAL_PORT = DP
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#
diff --git a/configs/haswell_ult b/configs/haswell_ult
new file mode 100644
index 0000000..f3deac5
--- /dev/null
+++ b/configs/haswell_ult
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Haswell
+CONFIG_GFX_GMA_CPU_VARIANT = ULT
+CONFIG_GFX_GMA_INTERNAL_PORT = DP
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#
diff --git a/configs/ironlake b/configs/ironlake
new file mode 100644
index 0000000..7450d35
--- /dev/null
+++ b/configs/ironlake
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Ironlake
+CONFIG_GFX_GMA_CPU_VARIANT = Normal
+CONFIG_GFX_GMA_INTERNAL_PORT = LVDS
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#
diff --git a/configs/ivybridge_edp b/configs/ivybridge_edp
new file mode 100644
index 0000000..53907a0
--- /dev/null
+++ b/configs/ivybridge_edp
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Ivybridge
+CONFIG_GFX_GMA_CPU_VARIANT = Normal
+CONFIG_GFX_GMA_INTERNAL_PORT = DP
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#
diff --git a/configs/ivybridge_lvds b/configs/ivybridge_lvds
new file mode 100644
index 0000000..6a77aea
--- /dev/null
+++ b/configs/ivybridge_lvds
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Ivybridge
+CONFIG_GFX_GMA_CPU_VARIANT = Normal
+CONFIG_GFX_GMA_INTERNAL_PORT = LVDS
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#
diff --git a/configs/sandybridge b/configs/sandybridge
new file mode 100644
index 0000000..50a6c61
--- /dev/null
+++ b/configs/sandybridge
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Sandybridge
+CONFIG_GFX_GMA_CPU_VARIANT = Normal
+CONFIG_GFX_GMA_INTERNAL_PORT = LVDS
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#
diff --git a/configs/skylake b/configs/skylake
new file mode 100644
index 0000000..7867f79
--- /dev/null
+++ b/configs/skylake
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Skylake
+CONFIG_GFX_GMA_CPU_VARIANT = Normal
+CONFIG_GFX_GMA_INTERNAL_PORT = DP
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#
diff --git a/configs/skylake_ult b/configs/skylake_ult
new file mode 100644
index 0000000..8c386db
--- /dev/null
+++ b/configs/skylake_ult
@@ -0,0 +1,4 @@
+CONFIG_GFX_GMA_CPU = Skylake
+CONFIG_GFX_GMA_CPU_VARIANT = ULT
+CONFIG_GFX_GMA_INTERNAL_PORT = DP
+CONFIG_GFX_GMA_DEFAULT_MMIO = 16#e000_0000#