commit 83693c8d7d87f5cebe120abdf25951c9e212b319 Author: Nico Huber Date: Sat Oct 8 22:17:55 2016 +0200 Initial upstream commit The history contained unlicensed code so everything got squashed, sorry. Change-Id: I9f5775208f9df6fb29074bf3bc498f68cb17b3a0 Signed-off-by: Nico Huber 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. + + + Copyright (C) + + 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. + + , 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/<>/$(CONFIG_GFX_GMA_CPU)/' \ + -e's/<>/$(CONFIG_GFX_GMA_CPU_VARIANT)/' \ + -e's/<>/$(CONFIG_GFX_GMA_INTERNAL_PORT)/' \ + -e's/<>/$(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 + -- + + 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_Var : constant CPU_Variant := <>; + + Internal_Display : constant Internal_Type := <>; + + EDP_Low_Voltage_Swing : constant Boolean := False; + + Default_MMIO_Base : constant := <>; + + 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 +-- +-- 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 +-- +-- 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 +-- +-- 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#