diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000000..dfd7a97966be --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +deps/rpi-rgb-led-matrix/** -whitespace diff --git a/Makefile b/Makefile index 89ebb389e721..d1f9d07d48a9 100644 --- a/Makefile +++ b/Makefile @@ -177,6 +177,13 @@ endif all: $(TARGET) $(METALLIB) config.mk +ifeq ($(HAVE_HUB75), 1) +hub75-library: + $(MAKE) -C deps/rpi-rgb-led-matrix/lib CC="$(CC)" CXX="$(CXX)" AR="$(HUB75_AR)" CPU_ARCH_FLAGS= librgbmatrix.a +$(HUB75_LIBRARY): | hub75-library +.PHONY: hub75-library +endif + define INFO ASFLAGS: $(ASFLAGS) CC: $(CC) @@ -230,7 +237,7 @@ endif SYMBOL_MAP := -Wl,-Map=output.map -$(TARGET): $(RARCH_OBJ) +$(TARGET): $(RARCH_OBJ) $(HUB75_LIBRARY) @$(if $(Q), $(shell echo echo LD $@),) $(Q)$(LINK) -o $@ $(RARCH_OBJ) $(LIBS) $(LDFLAGS) $(LIBRARY_DIRS) diff --git a/Makefile.common b/Makefile.common index b86581e69ec1..b79968a09159 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1519,6 +1519,16 @@ ifeq ($(HAVE_NETWORK_VIDEO), 1) OBJ += gfx/drivers/network_gfx.o endif +ifeq ($(HAVE_HUB75), 1) + DEFINES += -DHAVE_HUB75 + INCLUDE_DIRS += -Ideps/rpi-rgb-led-matrix/include + OBJ += gfx/drivers/hub75_gfx.o + HUB75_LIBRARY := deps/rpi-rgb-led-matrix/lib/librgbmatrix.a + HUB75_AR := $(if $(filter %gcc,$(CC)),$(patsubst %gcc,%ar,$(CC)),$(AR)) + LIBS += $(HUB75_LIBRARY) -lrt -lm -lpthread + NEED_CXX_LINKER = 1 +endif + ifeq ($(HAVE_PLAIN_DRM), 1) OBJ += gfx/drivers/drm_gfx.o ifneq ($(HAVE_LAKKA), 1) diff --git a/deps/rpi-rgb-led-matrix/COPYING b/deps/rpi-rgb-led-matrix/COPYING new file mode 100644 index 000000000000..d159169d1050 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/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/deps/rpi-rgb-led-matrix/README.md b/deps/rpi-rgb-led-matrix/README.md new file mode 100644 index 000000000000..ddba7f39e209 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/README.md @@ -0,0 +1,987 @@ +Controlling RGB LED display with Raspberry Pi GPIO +================================================== + +A library to control commonly available 128x64, 64x64, 32x32 or 16x32 RGB LED panels +with the Raspberry Pi. Can support PWM up to 11Bit per channel, providing +true 24bpp color with CIE1931 profile. + +Supports 3 chains with many panels each on a regular Pi. +On a Raspberry Pi 2 or 3, you can easily chain 12 panels in that chain +(so 36 panels total), but you can theoretically stretch that to up +to 96-ish panels (32 chain length) and still reach +around 100Hz refresh rate with full 24Bit color (theoretical - never tested +this; there might likely be timing problems with the panels that will creep +up then). + +With fewer colors or so-called 'outdoor panels' you can control even more, +faster. + +The LED-matrix library is (c) Henner Zeller , licensed with +[GNU General Public License Version 2.0 (or any later version)](http://www.gnu.org/licenses/gpl-2.0.txt) +(which means, if you use it in a product somewhere, you need to make the +source and all your modifications available to the receiver of such product so +that they have the freedom to adapt and improve). +Yes, you are also allowed to use +this code under the [GNU General Public License Version 3.0](http://www.gnu.org/licenses/gpl-3.0.txt) +which allows linking with Apache 2.0 code and LGPL-3.0 code. + +## Discourse discussion group + +**If you'd like help, please do not file a bug, use the discussion board instead:** +https://rpi-rgb-led-matrix.discourse.group/ (obviously please read this whole page first). +If you file a bug asking for personal help instead of using the discourse group, please +state in your bug that you have read this entire page and that you're indeed filing a bug +or request for improvement. Otherwise, please use the discourse group. + +Overview +-------- +The RGB LED matrix panels can be scored at [Sparkfun][sparkfun], +[AdaFruit][ada] or eBay and Aliexpress. If you are in China, I'd try to get +them directly from some manufacturer, Taobao or Alibaba. + +The `RGBMatrix` class provided in `include/led-matrix.h` does what is needed +to control these. You can use this as a library in your own projects or just +use the demo binary provided here which provides some useful examples. + +Check out [utils/ directory for some ready-made tools](./utils) to get started +using the library, or the [examples-api-use/](./examples-api-use) directory if +you want to get started programming your own utils. + +Panels supported +---------------- +This library does not support PWM panels (which actually are better, but need +a completely different driver).\ +It should support most other panels with direct addressing (3 to 5 address lines, +ABC for 8x2=16 lines, ABCD for 16x2=32 lines, and ABCDE for 32x2=64 lines). +It also supports panels using shift registers for line addressing, also called ABC +panels up to 64 lines.\ +However some amount of panels have uncommon pixel layouts, especially the brighter +panels and may require special pixel mappers (some are called zigzag or zagzig, see +https://rpi-rgb-led-matrix.discourse.group/t/p10rgb-3535-4s-zaggiz-1-4scan-abc-p10-panels-and-https-github-com-2dom-pxmatrix/921 ) + +Until this wiki has its own confirmed list of working panels, you can refer to +this list: https://github.com/board707/DMD_STM32/wiki/Led_drivers and assume +that all supported panels there should work with this library, or could work +with minimal work to create a pixel mapper. +Please do refer to --led-multiplexing=<0..17> mentioned below and try all values. \ +2025/11 update: DMD_STM32 has started adding experimental support for some PWM panels. +These should be labelled as "special" in the table above. They are not supported by this +lib, as it only supports non PWM panels (until someone contributes PWM support). + +PWM/E-PWM/S-PWM Panels +---------------------- +Newer PWM panels are not currently supported by this lib, but support would really be appreciated. +- https://github.com/hzeller/rpi-rgb-led-matrix/issues/466 - Master bug tracking PWM efforts +- https://github.com/hzeller/rpi-rgb-led-matrix/issues/1866 - Continued discussion + +Some experimentation and success has been made with the following fork. Some panel support with FM6373, FM6363, SM16380SH +- https://github.com/kingdo9/rpi-rgb-led-matrix_pwm_experiment + + +Raspberry Pi 1-5 supported +------------------------------ +This library supports the old Raspberry Pi's Version 1 with 26 pin header and +also the B+ models, the Pi Zero, Raspberry Pi 2,3,4 and 5 with 40 pins, as well +as the Compute Modules which have 44 GPIOs. + +#### Raspberry Pi 5 support has now been added. +There are two modes to run this on the Pi 5 + +**--led-rp1-pio=0** - Uses the Pi 5 RP1 RIO backend (default) - Higher CPU usage but much faster performance. + +**--led-rp1-pio=1** - Uses the Pi 5 RP1 PIO backend - Very minimal CPU usage. +**NOTE: --led-slowdown-gpio** can have the opposite effect in RIO mode, so going from 2 to 3 may increase performance slightly with --led-rp1-pio=0. + +Pi 5 Discussion - https://github.com/hzeller/rpi-rgb-led-matrix/issues/1603 + + +Thanks to contributions from the following libraries to help implement this. +https://github.com/adafruit/Adafruit_Blinka_Raspberry_Pi5_Piomatter \ +https://github.com/bitslip6/rpi-gpu-hub75-matrix \ +https://www.i-programmer.info/programming/148-hardware/16887-raspberry-pi-iot-in-c-pi-5-memory-mapped-gpio.html + +The 26 pin models can drive one chain of RGB panels, the 40 pin models +**up to three** chains in parallel (each chain 12 or more panels long). +The Compute Module can drive **up to 6 chains in parallel**. +The Raspberry Pi 2 and 3 are faster and generally preferred to the older +models (and the Pi Zero). With the faster models, the panels sometimes +can't keep up with the speed; check out +this [troubleshooting section](#troubleshooting) what to do. + +A lightweight, non-GUI, distribution such as [DietPi] is recommended. +[Raspbian Lite][raspbian-lite] is a bit easier to get started with and +is a good second choice. + +Types of Displays +----------------- +There are various types of displays that come all with the same Hub75 connector. +They vary in the way the multiplexing is happening so this library supports +options to choose that. +All these are configured by flags (or, programmatically, in an [Options struct](include/led-matrix.h#L57)). + +If you have a 64x32 display, you need to supply the flags +`--led-cols=64 --led-rows=32` for instance. + +Depending on the Matrix, there are various configuration options that +you might need to set for it to work. See further below in the README for the +[detailed description of these](#changing-parameters-via-command-line-flags). +While the `--led-rows` and `--led-cols` can be derived from simply looking +at the panels, the other options might require some experimenting to find the +right setting if there is no description provided by the manufacturer of +the panel. Going through these options for experiments would typically not do +harm, so you're free to experiment to find your setting. + +Flag                                | Description +:--------------- | :----------------- +`--led-cols` | Columns in the LED matrix, the 'width'. +`--led-rows` | Rows in the LED matrix, the 'height'. +`--led-multiplexing` | In particular bright outdoor panels with small multiplex ratios require this. Often an indicator: if there are fewer address lines than expected: ABC (instead of ABCD) for 32 high panels and ABCD (instead of ABCDE) for 64 high panels. +`--led-row-addr-type` | Addressing of rows; in particular panels with only AB address lines might indicate that this is needed. +`--led-panel-type` | Chipset of the panel. In particular if it doesn't light up at all, you might need to play with this option because it indicates that the panel requires a particular initialization sequence. + +Panels can be chained by connecting the output of one panel to the input of +the next panel. You can chain quite a few together, but the refresh rate will +reduce with longer chains. + +The 64x64 matrixes typically come in two kinds: with 5 address +lines (A, B, C, D, E), or (A, B); the latter needs a `--led-row-addr-type=1` +parameter. So-called 'outdoor panels' are typically brighter and allow for +faster refresh-rate for the same size, but do some multiplexing internally +of which there are a few types out there; they can be chosen with +the `--led-multiplexing` parameter. +Many 128x64 panels now use ABC addressing using shift registers, instead of direct +ABCDE addressing. Look at using --led-row-addr-type=5 or --led-row-addr-type=3 +with them (and you may need to use --led-slowdown-gpio of 2 or more. Please +see this bug for more details: https://github.com/hzeller/rpi-rgb-led-matrix/issues/1774 + +There are some panels that have a different chip-set than the default HUB75. +These require some initialization sequence. The current supported types are +`--led-panel-type=FM6126A` and `--led-panel-type=FM6127`. + +Generally, the higher scan-rate (e.g. 1:8), a.k.a. outdoor panels generally +allow faster refresh rate, but you might need to figure out the multiplexing +mapping if one of the three provided does not work. + +Some 32x16 outdoor matrixes with 1:4 scan (e.g. [Qiangli Q10(1/4) or X10(1/4)](http://qiangliled.com/products-63.html)) +have 4 address line (A, B, C, D). For such matrices is necessary to +use `--led-row-addr-type=2` parameter. Also the matrix Qiangli Q10(1/4) +have "Z"-stripe pixel mapping and in this case, you'd use two parameters +at the same time `--led-row-addr-type=2 --led-multiplexing=4`. + +Sub-Pages +--------- +This documentation is split into parts that help you through the process + +- + [**Wire up the matrix to your Pi**](./wiring.md). This document describes + what goes where. +- [How to map pixels between panels or within panels](./lib). This is crucial for figuring out pixel mappers, + matrix mappers and so forth. This is where you will learn about panel layout with U-Mapper, V-Mapper, V-Mapper:Z +- [Adapter GPIO boards output to up to 3 channels (electrodragon board recommended)](./adapter). + If you have an [Adafruit HAT] or [Adafruit Bonnet], you can choose that with + a command line option [described below](#if-you-have-an-adafruit-hat-or-bonnet) +- [All the command line options you can give to demo and in turn use in your library code](./examples-api-use). +- [Installing an Optimized Kernel to fix most flickering issues](./optimized-kernel) + +Python Support +-------------- +The python bindings have been overhauled as of February 2026 and are now built with scikit-build-core and cmake. While +this update has been tested there are likely edge cases that have not been caught. Please subscribe to the master Python +bug to assist or check for the latest updates. +https://github.com/hzeller/rpi-rgb-led-matrix/issues/1749. + +The entire repository itself is PIP-able and can be installed as a package directly. Python dev packages are required. +``` +sudo apt-get install python-dev-is-python3 python3-pil cython3 +pip install git+https://github.com/hzeller/rpi-rgb-led-matrix +``` + +Rpi Hardware Support +-------------------- +While this code should still work on rPi1 or Rpi0, those are underpowered and not recommended. An ESP32 is cheaper and faster. +Rpi2 is honestly old and slow too. +Rpi3 / Rpi3a / Rpi0 2wl will work for most displays but will be slow if you are rotating a huge display in a mapper (Rpi0 2wl is a slightly faster version of Rpi3) +Rpi4 is the fastest supported chip which will need a higher slowdown gpio value to avoid overwhelming panels (if they display garbage) +Rpi5 is not officially supported as of 2025/11, but see https://github.com/hzeller/rpi-rgb-led-matrix/issues/1603 + +Let's do it +------------ + 1. Run a demo. You find that in the + [examples-api-use/](./examples-api-use#running-some-demos) directory: +``` +make -C examples-api-use +sudo examples-api-use/demo -D0 +``` + 2. Use the utilities. The [utils](./utils) directory has some ready-made + useful utilities to show content. [Go there](./utils) to see how to + compile and run these. + 3. Write your own programs using the Matrix in C++ or one of the + bindings such as Python or C#. + +### Wiring / Boards + +Please see the [Adadpter Boards or Self Wiring](./adapter). + +Summary is: +- Yes you can self wire without level shifters and it will work most of the time, but if you're not in a hurry get a board +- https://www.electrodragon.com/product/rgb-matrix-panel-drive-board-for-raspberry-pi-v2/ **is the recommended solution with 3 channels and level shifters**. You can't go wrong there, but expect a bit of shipping time. +- If shipping time is crucial and you don't want to wire your own, Adafruit sells a single channel board (the electrodragon one is 3 channels), but note that its wiring is non standard and requires a special compile option or command line argument: https://www.adafruit.com/product/3211 + +### Utilities + +The [utils directory](./utils) is meant for ready utilities to show images or +animated gifs or videos. Read the [README](./utils/README.md) there for +instructions how to compile. + +There are external projects that use this library and provide higher level +network protocols, such as the + * [FlaschenTaschen implementation](https://github.com/hzeller/flaschen-taschen) + (VLC can send videos to it natively) + * [PixelPusher implementation](https://github.com/hzeller/rpi-matrix-pixelpusher) (common in light art installations) + * [ZeroMQ-server](https://github.com/Knifa/led-matrix-zmq-server) to receive + content. + * Marc's [FastLED_RPIRGBPanel_GFX](http://marc.merlins.org/perso/arduino/post_2020-01-01_Running-FastLED_-Adafruit_GFX_-and-LEDMatrix-code-on-High-Resolution-RGBPanels-with-a-Raspberry-Pi.html) allows running arduino code on linux/rPi and display on bigger RGBPanel matrices than arduino chips, can. + +### API + +The library comes as an API that you can use for your own utilities and use-cases. + + * The native library is a C++ library (see [include/](./include)). + Example uses you find in the [examples-api-use/](./examples-api-use) + directory. + * If you prefer to program in C, there is also a + [C API](./include/led-matrix-c.h). + * In the [python](./bindings/python) subdirectory, you find a Python API including a + couple of [examples](./bindings/python/samples) to get started. + * There are a couple of external bindings, such as + * [Nodejs binding] by Maxime Journaux + * [Nodejs/Typescript binding] by Alex Eden + * [Go binding] by Máximo Cuadros + * [Rust binding] by Vincent Pasquier + +### Changing parameters via command-line flags + +For the programs in this distribution and also automatically in your own +programs using this library, there are a lot of parameters provided as command +line flags, so that you don't have to re-compile your programs to tweak them. +Some might need to be changed for your particular kind of panel. + +Here is a little run-down of what these command-line flags do and when you'd +like to change them. + +First things first: if you have a different wiring than described in +[wiring](./wiring.md), for instance if you have an Adafruit HAT/Bonnet, you can +choose these here: + +``` +--led-gpio-mapping=: Name of GPIO mapping used. Default "regular" +``` + +This can have values such as + - `--led-gpio-mapping=regular` The standard mapping of this library, described in the [wiring](./wiring.md) page. + - `--led-gpio-mapping=adafruit-hat` The Adafruit HAT/Bonnet, that uses this library or + - `--led-gpio-mapping=adafruit-hat-pwm` Adafruit HAT with the anti- hardware mod [described below](#improving-flicker-hardware-patch). + - `--led-gpio-mapping=compute-module` Additional 3 parallel chains can be used with the Compute Module. + +Learn more about the mappings in the [wiring documentation](wiring.md#alternative-hardware-mappings). + +#### GPIO speed + +``` +--led-slowdown-gpio=<0..10>: Slowdown GPIO. Needed for faster Pis and/or slower panels (Default: 1). +``` + +The Raspberry Pi starting with Pi2 are putting out data too fast for almost +all LED panels I have seen. In this case, you want to slow down writing to +GPIO. Zero for this parameter means 'no slowdown'. + +The default 1 (one) typically works fine, but often you have to even go further +by setting it to 2 (two). If you have a Raspberry Pi with a slower processor +(Model A, A+, B+, Zero), then a value of 0 (zero) might work and is desirable. + +If you find yourself needing very high values of slowdown like 8 for ABC 128x64 panels, +do try --led-row-addr-type=5 instead of --led-row-addr-type=3. Details in this bug: +https://github.com/hzeller/rpi-rgb-led-matrix/issues/1773 + +A Raspberry Pi 3 or Pi4 might even need higher values for the panels to be +happy. + +#### Panel Connection +The next most important flags describe the type and number of displays connected + +``` +--led-rows= : Panel rows. Typically 8, 16, 32 or 64. (Default: 32). +--led-cols= : Panel columns. Typically 32 or 64. (Default: 32). +--led-chain= : Number of daisy-chained panels. (Default: 1). +--led-parallel=: For A/B+ models or RPi2,3b: parallel chains. range=1..3 (Default: 1, 6 for Compute Module). +``` + +These are the most important ones: here you choose how many panels you have +connected and how many rows are in each panel. Panels can be chained (each panel +has an input and output connector, see the +[wiring documentation](wiring.md#chains)) -- the `--led-chain` flag tells the +library how many panels are chained together. The newer Raspberry Pi's allow +to connect multiple chains in parallel, the `--led-parallel` flag tells it how +many there are. + +This illustrates what each of these parameters mean: + + + +##### Panel Type + +Typically, panels should just work out of the box, but some panels use a +different chip-set that requires some initialization. If you don't see any +output on your panel, try setting: + +``` +--led-panel-type=FM6126A +``` + +Some panels have the FM6127 chip, which is also an option. + +##### Multiplexing +If you have some 'outdoor' panels or panels with different multiplexing, +the following will be useful: + +``` +--led-multiplexing=<0..17> : Mux type: 0=direct; 1=Stripe; 2=Checkered... +``` + +The outdoor panels have different multiplexing which allows them to be faster +and brighter, but by default their output looks jumbled up. +They require some pixel-mapping of which there are a few +types you can try and hopefully one of them works for your panel; The default=0 +is no mapping ('standard' panels), while 1, 2, ... are different mappings +to try with. If your panel has a different mapping, you find everything you +need to implement one in [lib/multiplex-mappers.cc](lib/multiplex-mappers.cc). +Please send a pull request if you encounter a panel for which you needed to +implement a new mapping. + +Note that you have to set the `--led-rows` and `--led-cols` to the rows and +columns that are physically on each chained panel so that the multiplexing +option can work properly. For instance a `32x16` panel with `1:4` multiplexing +would be controlled with `--led-rows=16 --led-cols=32 --led-multiplexing=1` (or +whatever multiplexing type your panel is, so it can also be `--led-multiplexing=2` ...). + +For `64x32` panels with `1:8` multiplexing, this would typically be +`--led-rows=32 --led-cols=64 --led-multiplexing=1`; +however, there are some panels that internally behave like +two chained panels, so then you'd use +`--led-rows=32 --led-cols=32 --led-chain=2 --led-multiplexing=1`; + +``` +--led-row-addr-type=<0..5>: 0 = default; 1 = AB-addressed panels; 2 = direct row select; 3 = ABC-addressed panels; 4 = ABC Shift + DE direct, 5 = ABC method similar to 3, but faster on some panels (needs less of a slowdown) (Default: 0). +``` + +This option is useful for certain 64x64 or 32x16 panels. For 64x64 panels, +that only have an `A` and `B` address line, you'd use `--led-row-addr-type=1`. +This is only tested with one panel so far, so if it doesn't work for you, +please send a pull request. + +For 32x16 outdoor panels, that have have 4 address line (A, B, C, D), it is +necessary to use `--led-row-addr-type=2`. + +Performance improvements and limits, Maximizing refresh rate, ABC vs ABCDE, row-addr-type=5 vs 3 +------------------------------------------------------------------------------------------------- +Regardless of which driving hardware you use, ultimately you can only push pixels +so fast to a string of panels before you get flickering due to too low a refresh +rate (less than 80-100Hz), or before you refresh the panel lines too fast and they +appear too dim because each line is not displayed long enough before it is turned off. + +Basic performance tips: +- Use --led-show-refresh to see the refresh rate while you try parameters +- use an active-3 board with led-parallel=3 any time possible instead of chaining panels. +- led-pwm-dither-bits=1 gives you a speed boost but potentially less brightness +- led-pwm-lsb-nanoseconds=50 also gives you a speed boost but may lead to less brightness +- led-pwm-bits=7 or even lower decrease color depth but increases refresh speed +- AB panels and other panels with that use values of led-multiplexing bigger than 0, +will also go faster, although as you tune more options given above, their advantage will decrease. +- 32x16 ABC panels are faster than ABCD which are faster than ABCDE, which are faster than 128x64 ABC panels +(which do use 5 address lines, but over only 3 wires) +- For 128x64 ABC speed on shift registers vs ABCDE +- Use at least an rPi3 (rPi4 is still slightly faster but may need --led-slowdown-gpio=2) + +Maximizing refresh rate to 300 or even 400Mhz refresh for 3x 128x64 panels: +- ABCDE panels will allow for faster refresh rate than ABC panels because they can be run with +lower --led-slowdown-gpio=1 (which may need to be 2 for ABCDE) on rPi3 (rPi4 may require bigger +slowdowns. You may even end up in a situation where a Pi3 will give you faster refresh than a Pi4 +by needing smaller slowdowns +- To achieve 410Hz refresh for 3x 128x64 direct ABCDE panels, use this "~/rpi-rgb-led-matrix/examples-api-use/demo --led-rows=64 --led-cols=128 --led-chain=1 --led-parallel=3 --led-show-refresh --led-scan-mode=0 --led-pwm-bits=7 --led-pwm-lsb-nanoseconds=50 --led-pwm-dither-bits=1 -D0 --led-row-addr-type=0 --led-slowdown-gpio=1" +- To achieve 350hz refresh for 3x 128x64 shift register panels, use this: "~/rpi-rgb-led-matrix/examples-api-use/demo --led-rows=64 --led-cols=128 --led-chain=1 --led-parallel=3 --led-show-refresh --led-scan-mode=0 --led-pwm-bits=7 --led-pwm-lsb-nanoseconds=50 --led-pwm-dither-bits=1 -D0 --led-row-addr-type=5 --led-slowdown-gpio=2" + +Please see this bug for discussion of ABC vs ABCDE and using --led-row-addr-type=5 instead of --led-row-addr-type=3: +https://github.com/hzeller/rpi-rgb-led-matrix/issues/1774#issuecomment-2860617056 + +Maximum resolutions reasonably achievable: +A general rule of thumb is that running 16K pixels (128x128 or otherwise) on a single chain, +is already pushing limits and you will have to make tradeoffs in visual quality. 32K pixels +(like 128x256) is definitely pushing things and you'll get 100Hz or less depending on the +performance options you choose. +This puts the maximum reasonable resolution around 100K pixels (like 384x256) for 3 chains. +You can see more examples and video capture of speed on [Marc MERLIN's page 'RGB Panels, from 192x80, to 384x192, to 384x256 and maybe not much beyond'](http://marc.merlins.org/perso/arduino/post_2020-03-13_RGB-Panels_-from-192x80_-to-384x192_-to-384x256-and-maybe-not-much-beyond.html) +If your refresh rate is below 300Hz, expect likely black bars when taking cell phone pictures. +A real camera with shutter speed lowered accordingly, will get around this. + +Ultimately, you should not expect to go past 64K pixels using 3 chains without significant +quality tradeoffs. If you need bigger displays, you should use multiple boards and synchronize the +output. + + + + +#### Panel Arrangement + +``` +--led-pixel-mapper : Semicolon-separated list of pixel-mappers to arrange pixels. +``` + +Optional params after a colon e.g. "U-mapper;Rotate:90" + +Available | Parameter after colon| Example +----------|----------------------|---------- +Mirror | `H` or `V` for horizontal/vertical mirror. | `Mirror:H` +Rotate | Degrees. | `Rotate:90` +U-mapper | - + +Mapping the logical layout of your boards to your physical arrangement. See +more in [Remapping coordinates](./examples-api-use#remapping-coordinates). + +#### Misc Options + +``` +--led-brightness=: Brightness in percent (Default: 100). +``` + +Self explanatory. + + +``` +--led-pwm-bits=<1..11> : PWM bits (Default: 11). +``` + +The LEDs can only be switched on or off, so the shaded brightness perception +is achieved via PWM (Pulse Width Modulation). In order to get a good 8 Bit +per color resolution (24Bit RGB), the 11 bits default per color are good +(why ? Because our eyes are actually perceiving brightness logarithmically, so +we need a lot more physical resolution to get 24Bit sRGB). + +With this flag, you can change how many bits it should use for this; lowering it +means the lower bits (=more subtle color nuances) are omitted. +Typically you might be mostly interested in the extremes: 1 Bit for situations +that only require 8 colors (e.g. for high contrast text displays) or 11 Bit +for everything else (e.g. showing images or videos). Why would you bother at all ? +Lower number of bits use slightly less CPU and result in a higher refresh rate. + +``` +--led-show-refresh : Show refresh rate. +``` + +This shows the current refresh rate of the LED panel, the time to refresh +a full picture. Typically, you want this number to be pretty high, because the +human eye is pretty sensitive to flicker. Depending on the settings, the +refresh rate with this library are typically in the hundreds of Hertz but +can drop low with very long chains. Humans have different levels of perceiving +flicker - some are fine with 100Hz refresh, others need 250Hz. +So if you are curious, this gives you the number (shown on the terminal). + +The refresh rate depends on a lot of factors, from `--led-rows` and `--led-chain` +to `--led-pwm-bits`, `--led-pwm-lsb-nanoseconds` and `--led-pwm-dither-bits`. +If you are tweaking these parameters, showing the refresh rate can be a +useful tool. + +``` +--led-limit-refresh= : Limit refresh rate to this frequency in Hz. Useful to keep a + constant refresh rate on loaded system. 0=no limit. Default: 0 +``` + +This allows to limit the refresh rate to a particular frequency to approach +a fixed refresh rate. + +This can be used to mitigate some situations in which you have a faint flicker, +which can happen due to hardware events (network access) +or other situations such as other IO or heavy memory access by other +processes. Also when you see wildly changing refresh frequencies with +`--led-show-refresh`. + +You trade a slightly slower refresh rate and display brightness for less +visible flicker situations. + +For this to calibrate, run your program for a while with --led-show-refresh +and watch the line that shows the current refresh rate and minimum refresh +rate observed. So wait a while until that value doesn't +change anymore (e.g. a minute, so that you catch tasks that happen once +a minute, such as ntp updated). +Use this as a guidance what value to choose with `--led-limit-refresh`. + +The refresh rate will now be adapted to always reach this value +between frames, so faster refreshes will be slowed down, but the occasional +delayed frame will fit into the time-window as well, thus reducing visible +brightness fluctuations. + +You can play with value a little and reduce until you find a good balance +between refresh rate and flicker suppression. + +Use this also if you want to have a stable baseline refresh rate when using +the vsync-multiple flag `-V` in the [led-image-viewer] or +[video-viewer] utility programs. + +``` +--led-no-busy-waiting : Don't use busy waiting when limiting refresh rate. +``` + +This allows to switch from busy waiting to sleep waiting when limiting the +refresh rate (`--led-limit-refresh`). + +By default, refresh rate limiting uses busy waiting, which is CPU intensive but +gives most accurate timings. This is fine for multi-core boards. + +On single core boards (e.g.: Raspberry Pi Zero) busy waiting makes the system +unresponsive for other/background tasks. There, sleep waiting improves the +system's responsiveness at the cost of slightly less accurate timings. + +``` +--led-scan-mode=<0..1> : 0 = progressive; 1 = interlaced (Default: 0). +``` + +This switches from progressive scan and interlaced scan. The latter might +look be a little nicer when you have a very low refresh rate, but typically +it is more annoying because of the comb-effect (remember 80ies TV ?). + + +``` +--led-pwm-lsb-nanoseconds : PWM Nanoseconds for LSB (Default: 130) +``` + +This allows to change the base time-unit for the on-time in the lowest +significant bit in nanoseconds. +Lower values will allow higher frame-rate, but will also negatively impact +qualty in some panels (less accurate color or more ghosting). + +Good values for full-color display (PWM=11) are somewhere between 100 and 300. + +If you you use reduced bit color (e.g. PWM=1) and have sharp contrast +applications, then higher values might be good to minimize ghosting. + +How to decide ? Just leave the default if things are fine. But some panels have +trouble with sharp contrasts and short pulses that results +in ghosting. It is particularly apparent in situations such as bright text +on black background. In these cases increase the value until you don't see +this ghosting anymore. + +The following example shows how this might look like: + +Ghosting with low --led-pwm-lsb-nanoseconds | No ghosting after tweaking +---------------------------------------------|------------------------------ +![](img/text-ghosting.jpg) |![](img/text-no-ghosting.jpg) + +If you tweak this value, watch the framerate (`--led-show-refresh`) while playing +with this number. + +``` +--led-pwm-dither-bits : Time dithering of lower bits (Default: 0) +``` + +The lower bits can be time dithered, i.e. their brightness contribution is +achieved by only showing them some frames (this is possible, +because the PWM is implemented as binary code modulation). +This will allow higher refresh rate (or same refresh rate with increased +`--led-pwm-lsb-nanoseconds`). +The disadvantage could be slightly lower brightness, in particular for longer +chains, and higher CPU use. +CPU use is not of concern for Raspberry Pi 2 or 3 (as we run on a dedicated +core anyway) but probably for Raspberry Pi 1 or Pi Zero. +Default: no dithering; if you have a Pi 3 and struggle with low frame-rate due +to high multiplexing panels (1:16 or 1:32) or long chains, it might be +worthwhile to try. + +``` +--led-no-hardware-pulse : Don't use hardware pin-pulse generation. +``` + +This library uses a hardware subsystem that also is used by the sound. You can't +use them together. If your panel does not work, this might be a good start +to debug if it has something to do with the sound subsystem (see Troubleshooting +section). This is really only recommended for debugging; typically you actually +want the hardware pulses as it results in a much more stable picture. + + + +``` +--led-no-drop-privs : Don't drop privileges from 'root' after initializing the hardware. +``` + +You need to start programs as root as it needs to access some low-level hardware +at initialization time. After that, it is typically not desirable to stay in this +role, so the library then drops the privileges. + +This flag allows to switch off this behavior, so that you stay root. +Not recommended unless you have a specific reason for it (e.g. you need root +to access other hardware or you do the privilege dropping yourself). + +``` +--led-daemon : Make the process run in the background as daemon. +``` + +If this is set, the program puts itself into the background (running +as 'daemon'). +You might want this if started from an init script at boot-time. + +``` +--led-inverse : Switch if your matrix has inverse colors on. +--led-rgb-sequence : Switch if your matrix has led colors swapped (Default: "RGB") +``` + +These are if you have a different kind of LED panel in which the logic of the +color bits is reversed (`--led-inverse`) or where the Red, Green and Blue LEDs +are mixed up (`--led-rgb-sequence`). You know it when you see it. + +Troubleshooting +--------------- +Here are some tips in case things don't work as expected. + +### Use minimal Raspbian distribution +In general, run a minimal configuration on your Pi. + + * Common cause for flickering \ + **Ensure 5V is getting to LED Display and Raspberry Pi devices !!** \ + Use a Multimeter to check, also use dmesg command to check for any Raspberry Pi undervolt messages.\ + It may be best to power the LED Display externally especially if using Raspberry Pi 4. + + Additionally see [Installing an Optimized Kernel to fix most flickering issues](./optimized-kernel) + + * Do not use a graphical user interface (Even though the + Raspberry Pi foundation makes you believe that you can do that: don't. + Using a Pi with a GUI is a frustratingly slow use of an otherwise + perfectly good embedded device.). + Always operate your Raspberry Pi [headless]. + + * Switch off on-board sound + (`dtparam=audio=off` in `/boot/config.txt` pre-bookworm) + (`dtparam=audio=off` in `/boot/firmware/config.txt` post-bookworm) + External USB sound adapters work, and are much better quality anyway, + so that is recommended if you happen to need sound. The on-board sound + uses a timing circuit that the RGB-Matrix needs (it seems in some + distributions, such as arch-linux, this is not enough and you need + to explicitly blacklist the snd_bcm2835 module).\ + + post-bookworm \ + nano /etc/modprobe.d/blacklist-audio.conf \ + blacklist snd_bcm2835 \ + CTRL+S CTRL+X to Save File and Exit + + + * Don't run anything that messes in parallel with the GPIO pins, e.g. + PiGPIO library/daemon or devices that use the i2c or 1-wire interface if + they are on the same pins you need for the panel. + + * I have also seen reports that on some Pis, the one-wire protocol is + enabled (w1-gpio). This will also not work (disable by removing + `dtoverlay=w1-gpio` in `/boot/config.txt` (pre-bookworm) or in + `/boot/firmware/config.txt` (post-bookworm); or using `raspi-config`, + Interface Options -> 1-Wire) + + * If you see some regular flickering, make sure that there is no other + process running on the system that could cause that. For instance, it is + known that merely running `top` creates a faint flicker every second it + updates. Or a regular ntp run can also cause flicker once a minute + (switch off with `sudo timedatectl set-ntp false`). Maybe instead you + might want to run ntp at system start-up but then not regularly updating. + There might be other things running regularly you don't need; + consider a `sudo systemctl stop cron` for instance. + To address some irregular flicker, consider the + [`--led-limit-refresh`](#misc-options) option. + + * There are probably other processes that are running that you don't need + and remove them; I usually remove right away stuff I really don't need e.g. + ``` + sudo apt-get remove bluez bluez-firmware pi-bluetooth triggerhappy pigpio + ``` + Take a close look at your systemd (`systemctl`) and see if there are other + things running you don't need. If you have seen packages in standard + Raspbians that interfere with the matrix code, let me know to include it + here. + In general: This is why starting with a minimal installation is a good + idea: there is simply less cruft that you have to disable. + + * When attempting to connect sensors like the DHT22 to available GPIO pins + and display their results on your LED panel, you might encounter an issue + where the sensors fail to provide any data due to the Raspberry Pi resource + limitations. This situation is particularly evident on older models like the + Raspberry Pi 1 B. To address this, it is advisable to select a Raspberry Pi + model with higher resources to ensure proper functionality. + + +The default install of **[Raspbian Lite][raspbian-lite]** or **[DietPi]** +seem to be good starting points, as they have a reasonably minimal +configuration to begin with. Raspbian Lite is not as lite anymore +as it used to be; I prefer DietPi these days. Note that any of the above +changes to `/boot/...` must be applied to `/boot/firmware/...` if you are on a +non-Raspbian distro (e.g. Ubuntu for ARM). + +### Bad interaction with Sound +If sound is enabled on your Pi, this will not work together with the LED matrix, +as both need the same internal hardware sub-system (a first test to see if you +are affected is to run the progrem with `--led-no-hardware-pulse` and see if +things work fine then). + +If you run `lsmod` and see the `snd_bcm2835` module, this could be causing trouble. +(The library actually exits if it finds this module to be loaded). + +In that case, you should create a kernel module blacklist file like the +following on your system and update your initramfs: + +``` +cat < + +Then, start your programs with `--led-gpio-mapping=adafruit-hat-pwm`. + +Now you should have less visible flicker. This essentially +switches on the hardware pulses feature for the Adafruit HAT/Bonnet. + +### Improving flicker: Optimized Kernel +See [Installing an Optimized Kernel to fix most flickering issues](./optimized-kernel) + + +### 64x64 with E-line on Adafruit HAT/Bonnet +There are LED panels that have 64x64 LEDs packed, but they need 5 address lines, +which is 1:32 multiplexing (they have an `E` address-line). The first generation +of the Adafruit HAT/Bonnet was not prepared for this, but it can be done with another +hardware mod. Beginning October 2018, Adafruit began selling an updated version of +the HAT that supports 64x64 panels simply by bridging two pads on the PCB with solder. + +You can identify which HAT you have by looking for the **Address E** pads, circled here: + + + +### New Adafruit RGB Matrix Hat (with Address E pads) + +Look for the Address E pads located between the HUB75 connector and Pi camera cutout. + +Melt a blob of solder between the center “E” pad the the “8” pad just above it +(for 64x64 matrices in the Adafruit shop)…*_or_* the “16” pad below (rare, for some +third-party 64x64 matrices…check datasheet). + +### Old Adafruit HAT/Bonnet (without) + +Newer HAT/Bonnet versions have a special pad for the E-line next to the IDC socket +which makes this mod easily doable with a bit of solder. For details, refer to +[the Adafruit page on driving matrices](https://learn.adafruit.com/adafruit-rgb-matrix-plus-real-time-clock-hat-for-raspberry-pi/driving-matrices). + +For older HATs, the only option is a little more advanced hack, so it's only really +for people who are comfortable with this kind of thing. +First, you have to figure out which is the input of the E-Line on your matrix +(they seem to be either on Pin 4 or Pin 8 of the IDC connector). +You need to disconnect that Pin from the ground plane (e.g. with an Exacto +knife) and connect GPIO 24 to it. The following images illustrate the case for +IDC Pin 4. + + + + +If the direct connection does not work, you need to send it through a free +level converter of the Adafruit HAT/Bonnet. Since all unused inputs are grounded +with traces under the chip, this involves lifting a leg from the +HCT245 (figure out a free bus driver from the schematic). If all of the +above makes sense to you, you have the Ninja level to do it! + +It might be more convenient at this point to consider the [Active3 adapter](./adapter/active-3) +that has that covered already. + +Running as root +--------------- +The library requires to access hardware registers to control the LED matrix, +and create accurate timings. These hardware accesses require to run as root +user. + +For security reasons, it is usually not a good idea to run an application +as root entirely, so this library makes sure to drop privileges immediately +after the hardware is initialized. + +You can switch off the privilege dropping with the +[`--led-no-drop-privs`](#user-content-no-drop-priv) flag, or, if you do this +programmatically, +choose the configuration in the +[`RuntimeOptions struct`](https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/include/led-matrix.h#L401). + +Note, you _could_ run as non-root, which will use `/dev/gpiomem` +to at least write to GPIO, however the precise timing hardware registers are +not accessible. This will result in flicker and color degradation. Starting +as non-root is not recommended. + +CPU use +------- + +These displays need to be updated constantly to show an image with PWMed +LEDs. This is dependent on the length of the chain: for each chain element, +about 1'000'000 write operations have to happen every second! +(chain_length * 32 pixel long * 16 rows * 11 bit planes * 180 Hz refresh rate). + +We can't use hardware support for writing these as DMA is too slow, +thus the constant CPU use on an RPi is roughly 30-40% of one core. +Keep that in mind if you plan to run other things on this computer (This +is less noticeable on Raspberry Pi, Version 2 or 3 that has more cores). + +Also, the output quality is susceptible to other heavy tasks running on that +computer - there might be changes in the overall brightness when this affects +the refresh rate. + +If you have a loaded system and one of the newer Pis with 4 cores, you can +reserve one core just for the refresh of the display. Add: +``` +isolcpus=domain,managed_irq,3 nohz_full=3 rcu_nocbs=3 irqaffinity=0,1,2 +``` + +to the end of the line in `/boot/cmdline.txt` (pre-bookworm) or +`boot/firmware/cmdline.txt` (post-bookworm). It needs to be in the same line +line as the existing arguments -- no newline. This will use the last core +only to refresh the display then, but it also means, that no other process can +utilize it then. Still, I'd typically recommend it. + +This is just a partial fix, if you need better, please look at +[installing an Optimized Kernel to fix most flickering issues](./optimized-kernel) + +Limitations +----------- +If you are using the Adafruit HAT/Bonnet in the default configuration, then we +can't make use of the PWM hardware (which only outputs +to a particular pin), so you'll see random brightness glitches. I strongly +suggest to do the aforementioned hardware mod. + +The system needs constant CPU to update the display. Using the DMA controller +was considered but after extensive experiments +( https://github.com/hzeller/rpi-gpio-dma-demo ) +dropped due to its slow speed.. + +There is an upper limit in how fast the GPIO pins can be controlled, which +limits the frame-rate. Raspberry Pi 2's and newer are generally faster. + +Even with everything in place, you might see faint brightness fluctuations +in particular if there is something going on on the network or in a terminal +on the Pi; maybe there are also hardware limitations (memory bus +contention?). + +To address the brightness fluctuations, you might experiment with the +`FIXED_FRAME_MICROSECONDS` compile time option in [lib/Makefile](lib/Makefile) +that has instructions how to set it up. + +Fun +--- +I am always happy to see users successfully using the software for wonderful +things, like this installation by Dirk in Scharbeutz, Germany: + +![](./img/user-action-shot.jpg) + +A a few pictures from https://github.com/marcmerlin : +![thumb1024_102_20240629_288x192_RGBPanels_Google](https://github.com/user-attachments/assets/2adbef86-96bf-4ff8-afb4-493aa9174de7) +![thumb1024_137_20240629_288x192_RGBPanels_Google](https://github.com/user-attachments/assets/d87ca86c-2804-4aee-b6f2-f4c88145908c) +![prev1280_140_20240629_288x192_RGBPanels_Google](https://github.com/user-attachments/assets/2a621c91-4a84-4b4e-a9d2-092073cdee41) + +And probably the highest resolution build (384x256): +![thumb1024_429_20200310_FastLED_RPIRGBPanel_GFX_384x256](https://github.com/user-attachments/assets/60114825-efc4-4fef-88b0-749b53fa17b8) +![prev1280_551_20200310_FastLED_RPIRGBPanel_GFX_TableME_384x256](https://github.com/user-attachments/assets/94ea3c99-b751-4d12-8709-ef8b2bd197b9) + +https://www.youtube.com/watch?v=85PI2C6oBsQ + +[led-image-viewer]: ./utils#image-viewer +[video-viewer]: ./utils#video-viewer +[matrix64]: ./img/chained-64x64.jpg +[sparkfun]: https://www.sparkfun.com/products/12584 +[ada]: http://www.adafruit.com/product/1484 +[rt-paper]: https://www.osadl.org/fileadmin/dam/rtlws/12/Brown.pdf +[adafruit-hat]: https://www.adafruit.com/products/2345 +[raspbian-lite]: https://downloads.raspberrypi.org/raspbian_lite_latest +[DietPi]: https://dietpi.com/ +[Adafruit HAT]: https://www.adafruit.com/products/2345 +[Adafruit Bonnet]: https://www.adafruit.com/product/3211 +[Nodejs binding]: https://github.com/zeitungen/node-rpi-rgb-led-matrix +[Go binding]: https://github.com/mcuadros/go-rpi-rgb-led-matrix +[Rust binding]: https://crates.io/crates/rpi-led-matrix +[Nodejs/Typescript binding]: https://github.com/alexeden/rpi-led-matrix +[headless]: https://www.raspberrypi.com/documentation/computers/configuration.html#setting-up-a-headless-raspberry-pi +[how to build bigger displays]: https://marc.merlins.org/perso/arduino/post_2020-03-13_RGB-Panels_-from-192x80_-to-384x192_-to-384x256-and-maybe-not-much-beyond.html + +Maintenance +----------- +Henner as of 2025, isn't actively maintaining this project due to plenty of other projects that keep him busy :) +A few people have stepped him to help: +- https://github.com/marcmerlin is no expert on the codebase but is willing to merge safe looking and well tested patches +- https://github.com/board707 has provided valuable technical expertise on new panel types and chips as well as ABC mode 5 code for newer panels +- https://github.com/ty-porter and https://github.com/chrisgilldc have provided python binding expertise and help diff --git a/deps/rpi-rgb-led-matrix/README.retroarch.md b/deps/rpi-rgb-led-matrix/README.retroarch.md new file mode 100644 index 000000000000..207920a5086d --- /dev/null +++ b/deps/rpi-rgb-led-matrix/README.retroarch.md @@ -0,0 +1,14 @@ +# Vendored rpi-rgb-led-matrix + +This directory contains the subset of +[`hzeller/rpi-rgb-led-matrix`](https://github.com/hzeller/rpi-rgb-led-matrix) +required by RetroArch's HUB75 video driver. + +- Upstream commit: `d7eef803089d49606c9a22170cbd8bd6c97dc2a3` +- Upstream date: 2026-07-21 +- License: GPL-2.0-or-later (see `COPYING` and the upstream `README.md`) + +The vendored subset consists of the public headers, the static-library +sources, the Raspberry Pi 5 RP1 PIO/RIO backend, and upstream build metadata. +Examples, language bindings, fonts, utilities, and documentation images are +not required by RetroArch and are intentionally omitted. diff --git a/deps/rpi-rgb-led-matrix/config.mk b/deps/rpi-rgb-led-matrix/config.mk new file mode 100644 index 000000000000..a8e3df41c764 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/config.mk @@ -0,0 +1,20 @@ +# Choose LTO options that works with the chosen compiler. Also, the +# `ar` link archiver needs to be chosen as they are very specific to that +# version. +COMPILER_VERSION := $(shell $(CXX) --version 2>/dev/null) +ifneq (,$(findstring clang,$(COMPILER_VERSION))) + LTO_FLAGS=-flto=thin + AR=llvm-ar +else + LTO_FLAGS=-flto=2 + AR=gcc-ar +endif + +# When cross-compiling for aarch64 (e.g. using aarch64-linux-gnu-g++), +# the compiler does not accept '-march=native' or '-mtune=native'. Detect +# that situation and avoid those flags. For native builds keep the flags. +ifneq (,$(findstring aarch64-linux-gnu,$(CXX))) + CPU_ARCH_FLAGS= +else + CPU_ARCH_FLAGS=-march=native -mtune=native +endif diff --git a/deps/rpi-rgb-led-matrix/include/canvas.h b/deps/rpi-rgb-led-matrix/include/canvas.h new file mode 100644 index 000000000000..795219b35045 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/include/canvas.h @@ -0,0 +1,52 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2014 Henner Zeller +// +// 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. +// +// 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, see + +#ifndef RPI_CANVAS_H +#define RPI_CANVAS_H +#include + +namespace rgb_matrix { +// An interface for things a Canvas can do. The RGBMatrix implements this +// interface, so you can use it directly wherever a canvas is needed. +// +// This abstraction also allows you to e.g. create delegating +// implementations that do a particular transformation, e.g. re-map +// pixels (as you might lay out the physical RGB matrix in a different way), +// compose images (OR, XOR, transparency), scale, rotate, anti-alias or +// translate coordinates in a funky way. +// +// It is a good idea to have your applications use the concept of +// a Canvas to write the content to instead of directly using the RGBMatrix. +class Canvas { +public: + virtual ~Canvas() {} + virtual int width() const = 0; // Pixels available in x direction. + virtual int height() const = 0; // Pixels available in y direction. + + // Set pixel at coordinate (x,y) with given color. Pixel (0,0) is the + // top left corner. + // Each color is 8 bit (24bpp), 0 black, 255 brightest. + virtual void SetPixel(int x, int y, + uint8_t red, uint8_t green, uint8_t blue) = 0; + + // Clear screen to be all black. + virtual void Clear() = 0; + + // Fill screen with given 24bpp color. + virtual void Fill(uint8_t red, uint8_t green, uint8_t blue) = 0; +}; + +} // namespace rgb_matrix +#endif // RPI_CANVAS_H diff --git a/deps/rpi-rgb-led-matrix/include/content-streamer-c.h b/deps/rpi-rgb-led-matrix/include/content-streamer-c.h new file mode 100644 index 000000000000..06d672108522 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/include/content-streamer-c.h @@ -0,0 +1,34 @@ +/* C API for content-streamer (C wrapper for content-streamer.h) */ +#ifndef CONTENT_STREAMER_C_H +#define CONTENT_STREAMER_C_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Opaque type for StreamReader */ +typedef void* ContentStreamReaderHandle; + +/* Forward declarations */ +struct FrameCanvas; +struct StreamIO; + +/* Stream reader control */ +ContentStreamReaderHandle content_stream_reader_create(struct StreamIO* io); +void content_stream_reader_destroy(ContentStreamReaderHandle reader); +int content_stream_reader_get_next(ContentStreamReaderHandle reader, struct FrameCanvas* frame, uint32_t* hold_time_us); +void content_stream_reader_rewind(ContentStreamReaderHandle reader); + +/* FileStreamIO management */ +struct StreamIO* file_stream_io_create(const char* filename); +void file_stream_io_delete(struct StreamIO* io); + +int file_stream_io_is_compatible_with_canvas(struct StreamIO* io, struct FrameCanvas* frame); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/rpi-rgb-led-matrix/include/content-streamer.h b/deps/rpi-rgb-led-matrix/include/content-streamer.h new file mode 100644 index 000000000000..a2accb606695 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/include/content-streamer.h @@ -0,0 +1,138 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// +// Abstractions to read and write FrameCanvas objects to streams. This allows +// you to create canned streams of content with minimal overhead at runtime +// to play with extreme pixel-throughput which also minimizes overheads in +// the Pi to avoid stuttering or brightness glitches. +// +// The disadvantage is, that this represents the full expanded internal +// representation of a frame, so is very large memory wise. +// +// These abstractions are used in util/led-image-viewer.cc to read and +// write such animations to disk. It is also used in util/video-viewer.cc +// to write a version to disk that then can be played with the led-image-viewer. + +#ifndef RPI_CONTENT_STREAMER_H +#define RPI_CONTENT_STREAMER_H +#include +#include +#include +#include + +#include + +namespace rgb_matrix { +class FrameCanvas; + +// An abstraction of a data stream. Two implementations exist for files and +// an in-memory representation, but this allows your own implementation, e.g. +// reading from a socket. +class StreamIO { +public: + virtual ~StreamIO() {} + + // Rewind stream. + virtual void Rewind() = 0; + + // Read bytes into buffer at current position of stream. + // Similar to Posix behavior that allows short reads. + virtual ssize_t Read(void *buf, size_t count) = 0; + + // Write bytes from buffer. Similar to Posix behavior that allows short + // writes. + virtual ssize_t Append(const void *buf, size_t count) = 0; +}; + +class FileStreamIO : public StreamIO { +public: + explicit FileStreamIO(int fd); + ~FileStreamIO(); + + void Rewind() final; + ssize_t Read(void *buf, size_t count) final; + ssize_t Append(const void *buf, size_t count) final; + +private: + const int fd_; +}; + +// Storing a stream in memory. Owns the memory. +class MemStreamIO : public StreamIO { +public: + void Rewind() final; + ssize_t Read(void *buf, size_t count) final; + ssize_t Append(const void *buf, size_t count) final; + +private: + std::string buffer_; // super simplistic. + size_t pos_; +}; + +// Just a view around the memory, possibly a memory mapped file. +class MemMapViewInput : public StreamIO { +public: + MemMapViewInput(int fd); + ~MemMapViewInput(); + + // Since mmmap() might fail, this tells us if it was successful. + bool IsInitialized() const { return buffer_ != nullptr; } + + void Rewind() final; + ssize_t Read(void *buf, size_t count) final; + + // No append, this is purely read-only. + ssize_t Append(const void *buf, size_t count) final { return -1; } + +private: + char *buffer_; + char *end_; + char *pos_; +}; + +class StreamWriter { +public: + // Does not take ownership of StreamIO + StreamWriter(StreamIO *io); + + // Stream out given canvas at the given time. "hold_time_us" indicates + // for how long this frame is to be shown in microseconds. + bool Stream(const FrameCanvas &frame, uint32_t hold_time_us); + +private: + void WriteFileHeader(const FrameCanvas &frame, size_t len); + + StreamIO *const io_; + bool header_written_; +}; + +class StreamReader { +public: + // Does not take ownership of StreamIO + StreamReader(StreamIO *io); + ~StreamReader(); + + // Go back to the beginning. + void Rewind(); + + // Get next frame and its timestamp. Returns 'false' if there is an error + // or end of stream reached.. + bool GetNext(FrameCanvas *frame, uint32_t* hold_time_us); + +private: + enum State { + STREAM_AT_BEGIN, + STREAM_READING, + STREAM_ERROR, + }; + bool ReadFileHeader(const FrameCanvas &frame); + + StreamIO *io_; + size_t frame_buf_size_; + State state_; + + char *header_frame_buffer_; +}; +// Helpers for external C bridge wrappers. +bool StreamIOIsCompatibleWithCanvas(StreamIO* io, FrameCanvas* frame); +} // namespace rgb_matrix +#endif \ No newline at end of file diff --git a/deps/rpi-rgb-led-matrix/include/graphics.h b/deps/rpi-rgb-led-matrix/include/graphics.h new file mode 100644 index 000000000000..40b329ea473c --- /dev/null +++ b/deps/rpi-rgb-led-matrix/include/graphics.h @@ -0,0 +1,154 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Very simple graphics library to do simple things. +// +// Might be useful to consider using Cairo instead and just have an interface +// between that and the Canvas. Well, this is a quick set of things to get +// started (and nicely self-contained). +#ifndef RPI_GRAPHICS_H +#define RPI_GRAPHICS_H + +#include "canvas.h" + +#include +#include + +#include + +namespace rgb_matrix { +struct Color { + Color() : r(0), g(0), b(0) {} + Color(uint8_t rr, uint8_t gg, uint8_t bb) : r(rr), g(gg), b(bb) {} + uint8_t r; + uint8_t g; + uint8_t b; + void setColor(uint8_t rr, uint8_t gg, uint8_t bb) {r = rr; g = gg; b = bb;} +}; + +// Font loading bdf files. If this ever becomes more types, just make virtual +// base class. +class Font { +public: + // Initialize font, but it is only usable after LoadFont() has been called. + Font(); + ~Font(); + + bool LoadFont(const char *path); + bool ReadFont(const char *font_file_as_string); + + // Return height of font in pixels. Returns -1 if font has not been loaded. + int height() const { return font_height_; } + + // Return baseline. Pixels from the topline to the baseline. + int baseline() const { return base_line_; } + + // Return width of given character, or -1 if font is not loaded or character + // does not exist. + int CharacterWidth(uint32_t unicode_codepoint) const; + + // Draws the unicode character at position "x","y" + // with "color" on "background_color" (background_color can be NULL for + // transparency. + // The "y" position is the baseline of the font. + // If we don't have it in the font, draws the replacement character "�" if + // available. + // Returns how much we advance on the screen, which is the width of the + // character or 0 if we didn't draw any character. + int DrawGlyph(Canvas *c, int x, int y, + const Color &color, const Color *background_color, + uint32_t unicode_codepoint) const; + + // Same without background. Deprecated, use the one above instead. + int DrawGlyph(Canvas *c, int x, int y, const Color &color, + uint32_t unicode_codepoint) const; + + // Create a new font derived from this font, which represents an outline + // of the original font, essentially pixels tracing around the original + // letter. + // This can be used in situations in which it is desirable to frame a letter + // in a different color to increase contrast. + // The ownership of the returned pointer is passed to the caller. + Font *CreateOutlineFont() const; + +private: + Font(const Font& x); // No copy constructor. Use references or pointer instead. + + struct Glyph; + typedef std::map CodepointGlyphMap; + + const Glyph *FindGlyph(uint32_t codepoint) const; + + void parseLine(const char* buffer, Glyph* ¤t_glyph, uint32_t &codepoint, Glyph &tmp, int &row); + + int font_height_; + int base_line_; + CodepointGlyphMap glyphs_; +}; + +// -- Some utility functions. + +// Utility function: set an image from the given buffer containing pixels. +// +// Draw image of size "image_width" and "image_height" from pixel at +// canvas-offset "canvas_offset_x", "canvas_offset_y". Image will be shown +// cropped on the edges if needed. +// +// The canvas offset can be negative, i.e. the image start can be shifted +// outside the image frame on the left/top edge. +// +// The buffer needs to be organized as rows with columns of three bytes +// organized as rgb or bgr. Thus the size of the buffer needs to be exactly +// (3 * image_width * image_height) bytes. +// +// The "image_buffer" parameters contains the data, "buffer_size_bytes" the +// size in bytes. +// +// If "is_bgr" is true, the buffer is treated as BGR pixel arrangement instead +// of RGB. +// Returns 'true' if image was shown within canvas. +bool SetImage(Canvas *c, int canvas_offset_x, int canvas_offset_y, + const uint8_t *image_buffer, size_t buffer_size_bytes, + int image_width, int image_height, + bool is_bgr); + +// Draw text, a standard NUL terminated C-string encoded in UTF-8, +// with given "font" at "x","y" with "color". +// "color" always needs to be set (hence it is a reference), +// "background_color" is a pointer to optionally be NULL for transparency. +// "kerning_offset" allows for additional spacing between characters (can be +// negative) +// Returns how many pixels we advanced on the screen. +int DrawText(Canvas *c, const Font &font, int x, int y, + const Color &color, const Color *background_color, + const char *utf8_text, int kerning_offset = 0); + +// Same without background. Deprecated, use the one above instead. +int DrawText(Canvas *c, const Font &font, int x, int y, const Color &color, + const char *utf8_text); + +// Draw text, a standard NUL terminated C-string encoded in UTF-8, +// with given "font" at "x","y" with "color". +// Draw text as above, but vertically (top down). +// The text is a standard NUL terminated C-string encoded in UTF-8. +// "font, "x", "y", "color" and "background_color" are same as DrawText(). +// "kerning_offset" allows for additional spacing between characters (can be +// negative). +// Returns font height to advance up on the screen. +int VerticalDrawText(Canvas *c, const Font &font, int x, int y, + const Color &color, const Color *background_color, + const char *utf8_text, int kerning_offset = 0); + +// Figure out how many pixels long a bit of text will be when drawn +// Useful for aligning text to the center or right +// Return value will be the length of the text in pixels +// (ie. the width for normal horizontal text) +int MeasureText(const Font &font, const char *utf8_text, int kerning_offset = 0); + +// Draw a circle centered at "x", "y", with a radius of "radius" and with "color" +void DrawCircle(Canvas *c, int x, int y, int radius, const Color &color); + +// Draw a line from "x0", "y0" to "x1", "y1" and with "color" +void DrawLine(Canvas *c, int x0, int y0, int x1, int y1, const Color &color); + +} // namespace rgb_matrix + +#endif // RPI_GRAPHICS_H diff --git a/deps/rpi-rgb-led-matrix/include/led-matrix-c.h b/deps/rpi-rgb-led-matrix/include/led-matrix-c.h new file mode 100644 index 000000000000..2640e1ba6477 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/include/led-matrix-c.h @@ -0,0 +1,427 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- + * Copyright (C) 2013 Henner Zeller + * + * 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. + * + * 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, see + * + * Controlling 16x32 or 32x32 RGB matrixes via GPIO. It allows daisy chaining + * of a string of these, and also connecting a parallel string on newer + * Raspberry Pis with more GPIO pins available. + * + * This is a C-binding (for the C++ library) to allow easy binding and + * integration with other languages. The symbols are exported in librgbmatrix.a + * and librgbmatrix.so. You still need to call the final link with + * + * See examples-api-use/c-example.c for a usage example. + * + */ +#ifndef RPI_RGBMATRIX_C_H +#define RPI_RGBMATRIX_C_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct RGBLedMatrix; +struct LedCanvas; +struct LedFont; + +/** + * Parameters to create a new matrix. + * + * To get the defaults, non-set values have to be initialized to zero, so you + * should zero out this struct before setting anything. + */ +struct RGBLedMatrixOptions { + /* + * Name of the hardware mapping used. If passed NULL here, the default + * is used. + */ + const char *hardware_mapping; + + /* The "rows" are the number of rows supported by the display, so 32 or 16. + * Default: 32. + * Corresponding flag: --led-rows + */ + int rows; + + /* The "cols" are the number of columns per panel. Typically something + * like 32, but also 64 is possible. Sometimes even 40. + * cols * chain_length is the total length of the display, so you can + * represent a 64 wide display as cols=32, chain=2 or cols=64, chain=1; + * same thing. + * Flag: --led-cols + */ + int cols; + + /* The chain_length is the number of displays daisy-chained together + * (output of one connected to input of next). Default: 1 + * Corresponding flag: --led-chain + */ + int chain_length; + + /* The number of parallel chains connected to the Pi; in old Pis with 26 + * GPIO pins, that is 1, in newer Pis with 40 interfaces pins, that can + * also be 2 or 3. The effective number of pixels in vertical direction is + * then thus rows * parallel. Default: 1 + * Corresponding flag: --led-parallel + */ + int parallel; + + /* Set PWM bits used for output. Default is 11, but if you only deal with + * limited comic-colors, 1 might be sufficient. Lower require less CPU and + * increases refresh-rate. + * Corresponding flag: --led-pwm-bits + */ + int pwm_bits; + + /* Change the base time-unit for the on-time in the lowest + * significant bit in nanoseconds. + * Higher numbers provide better quality (more accurate color, less + * ghosting), but have a negative impact on the frame rate. + * Corresponding flag: --led-pwm-lsb-nanoseconds + */ + int pwm_lsb_nanoseconds; + + /* The lower bits can be time-dithered for higher refresh rate. + * Corresponding flag: --led-pwm-dither-bits + */ + int pwm_dither_bits; + + /* The initial brightness of the panel in percent. Valid range is 1..100 + * Corresponding flag: --led-brightness + */ + int brightness; + + /* Scan mode: 0=progressive, 1=interlaced + * Corresponding flag: --led-scan-mode + */ + int scan_mode; + + /* Default row address type is 0, corresponding to direct setting of the + * row, while row address type 1 is used for panels that only have A/B, + * typically some 64x64 panels + */ + int row_address_type; /* Corresponding flag: --led-row-addr-type */ + + /* Type of multiplexing. 0 = direct, 1 = stripe, 2 = checker (typical 1:8) + */ + int multiplexing; + + /** The following boolean flags are off by default **/ + + /* Allow to use the hardware subsystem to create pulses. This won't do + * anything if output enable is not connected to GPIO 18. + * Corresponding flag: --led-hardware-pulse + */ + bool disable_hardware_pulsing; /* Flag: --led-hardware-pulse */ + bool show_refresh_rate; /* Flag: --led-show-refresh */ + bool inverse_colors; /* Flag: --led-inverse */ + + /* In case the internal sequence of mapping is not "RGB", this contains the + * real mapping. Some panels mix up these colors. + */ + const char *led_rgb_sequence; /* Corresponding flag: --led-rgb-sequence */ + + /* A string describing a sequence of pixel mappers that should be applied + * to this matrix. A semicolon-separated list of pixel-mappers with optional + * parameter. + */ + const char *pixel_mapper_config; /* Corresponding flag: --led-pixel-mapper */ + + /* + * Panel type. Typically just NULL, but certain panels (FM6126) require + * an initialization sequence + */ + const char *panel_type; /* Corresponding flag: --led-panel-type */ + + /* Limit refresh rate of LED panel. This will help on a loaded system + * to keep a constant refresh rate. <= 0 for no limit. + */ + int limit_refresh_rate_hz; /* Corresponding flag: --led-limit-refresh */ + + /* Sleep instead of busy waiting when limiting refresh rate. This gives + * slightly less accurate frame timing, but lets the CPU work on other + * processes when waiting and renders single core boards more responsive. + */ + bool disable_busy_waiting; /* Corresponding flag: --led-busy-waiting */ +}; + +/** + * Runtime options to simplify doing common things for many programs such as + * dropping privileges and becoming a daemon. + */ +struct RGBLedRuntimeOptions { + int gpio_slowdown; // 0 = no slowdown. Flag: --led-slowdown-gpio + int rp1_pio; // 0 = default RP1 RIO. 1 = RP1 PIO. Flag: --led-rp1-pio + + // ---------- + // If the following options are set to disabled with -1, they are not + // even offered via the command line flags. + // ---------- + + // There are three possible values here + // -1 : don't leave choice of becoming daemon to the command line parsing. + // If set to -1, the --led-daemon option is not offered. + // 0 : do not becoma a daemon, run in forgreound (default value) + // 1 : become a daemon, run in background. + // + // If daemon is disabled (= -1), the user has to call + // RGBMatrix::StartRefresh() manually once the matrix is created, to leave + // the decision to become a daemon + // after the call (which requires that no threads have been started yet). + // In the other cases (off or on), the choice is already made, so the thread + // is conveniently already started for you. + int daemon; // -1 disabled. 0=off, 1=on. Flag: --led-daemon + + // Drop privileges from 'root' to 'daemon' once the hardware is initialized. + // This is usually a good idea unless you need to stay on elevated privs. + int drop_privileges; // -1 disabled. 0=off, 1=on. flag: --led-drop-privs + + // By default, the gpio is initialized for you, but if you run on a platform + // not the Raspberry Pi, this will fail. If you don't need to access GPIO + // e.g. you want to just create a stream output (see content-streamer.h), + // set this to false. + bool do_gpio_init; + + // If drop privileges is enabled, this is the user/group we drop privileges + // to. Unless chosen otherwise, the default is "daemon" for user and group. + const char *drop_priv_user; + const char *drop_priv_group; +}; + +/** + * 24-bit RGB color. + */ +struct Color { + uint8_t r; + uint8_t g; + uint8_t b; +}; + +/** + * Universal way to create and initialize a matrix. + * The "options" struct (if not NULL) contains all default configuration values + * chosen by the programmer to create the matrix. + * + * If "argc" and "argv" are provided, this function also reads command line + * flags provided, that then can override any of the defaults given. + * The arguments that have been used from the command line are removed from + * the argv list (and argc is adjusted) - that way these don't mess with your + * own command line handling. + * + * The actual options used are filled back into the "options" struct if not + * NULL. + * + * Usage: + * ---------------- + * int main(int argc, char **argv) { + * struct RGBLedMatrixOptions options; + * memset(&options, 0, sizeof(options)); + * options.rows = 32; // You can set defaults if you want. + * options.chain_length = 1; + * struct RGBLedMatrix *matrix = led_matrix_create_from_options(&options, + * &argc, &argv); + * if (matrix == NULL) { + * led_matrix_print_flags(stderr); + * return 1; + * } + * // do additional commandline handling; then use matrix... + * } + * ---------------- + */ +struct RGBLedMatrix *led_matrix_create_from_options( + struct RGBLedMatrixOptions *options, int *argc, char ***argv); + +/* Same, but does not modify the argv array. */ +struct RGBLedMatrix *led_matrix_create_from_options_const_argv( + struct RGBLedMatrixOptions *options, int argc, char **argv); + +/** + * The way to completely initialize your matrix without using command line + * flags to initialize some things. + * + * The actual options used are filled back into the "options" and "rt_options" + * struct if not NULL. If they are null, the default value is used. + * + * Usage: + * ---------------- + * int main(int argc, char **argv) { + * struct RGBLedMatrixOptions options; + * struct RGBLedRuntimeOptions rt_options; + * memset(&options, 0, sizeof(options)); + * memset(&rt_options, 0, sizeof(rt_options)); + * options.rows = 32; // You can set defaults if you want. + * options.chain_length = 1; + * rt_options.gpio_slowdown = 4; + * // To force Pi 5-family PIO from the C API: + * // rt_options.rp1_pio = 1; + * struct RGBLedMatrix *matrix = led_matrix_create_from_options_and_rt_options(&options, &rt_options); + * if (matrix == NULL) { + * return 1; + * } + * // do additional commandline handling; then use matrix... + * } + * ---------------- + */ +struct RGBLedMatrix *led_matrix_create_from_options_and_rt_options( + struct RGBLedMatrixOptions *opts, struct RGBLedRuntimeOptions * rt_opts); + +/** + * Print available LED matrix options. + */ +void led_matrix_print_flags(FILE *out); + +/** + * Simple form of led_matrix_create_from_options() with just the few + * main options. Returns NULL if that was not possible. + * The "rows" are the number of rows supported by the display, so 32, 16 or 8. + * + * Number of "chained_display"s tells many of these are daisy-chained together + * (output of one connected to input of next). + * + * The "parallel_display" number determines if there is one or two displays + * connected in parallel to the GPIO port - this only works with newer + * Raspberry Pi that have 40 interface pins. + * + * This creates a realtime thread and requires root access to access the GPIO + * pins. + * So if you run this in a daemon, this should be called after becoming a + * daemon (as fork/exec stops threads) and before dropping privileges. + */ +struct RGBLedMatrix *led_matrix_create(int rows, int chained, int parallel); + + +/** + * Stop matrix and free memory. + * Always call before the end of the program to properly reset the hardware + */ +void led_matrix_delete(struct RGBLedMatrix *matrix); + + +/** + * Get active canvas from LED matrix for you to draw on. + * Ownership of returned pointer stays with the matrix, don't free(). + */ +struct LedCanvas *led_matrix_get_canvas(struct RGBLedMatrix *matrix); + +/** Return size of canvas. */ +void led_canvas_get_size(const struct LedCanvas *canvas, + int *width, int *height); + +/** Set pixel at (x, y) with color (r,g,b). */ +void led_canvas_set_pixel(struct LedCanvas *canvas, int x, int y, + uint8_t r, uint8_t g, uint8_t b); + +/** Copies pixels to rectangle at (x, y) with size (width, height). */ +void led_canvas_set_pixels(struct LedCanvas *canvas, int x, int y, + int width, int height, struct Color *colors); + +/** Clear screen (black). */ +void led_canvas_clear(struct LedCanvas *canvas); + +/** Fill matrix with given color. */ +void led_canvas_fill(struct LedCanvas *canvas, uint8_t r, uint8_t g, uint8_t b); + +/** Fill subsection of matrix with given color. */ +void led_canvas_subfill(struct LedCanvas *canvas, int x, int y, + int width, int height, uint8_t r, uint8_t g, uint8_t b); +/*** API to provide double-buffering. ***/ + +/** + * Create a new canvas to be used with led_matrix_swap_on_vsync() + * Ownership of returned pointer stays with the matrix, don't free(). + */ +struct LedCanvas *led_matrix_create_offscreen_canvas(struct RGBLedMatrix *matrix); + +/** + * Swap the given canvas (created with create_offscreen_canvas) with the + * currently active canvas on vsync (blocks until vsync is reached). + * Returns the previously active canvas. So with that, you can create double + * buffering: + * + * struct LedCanvas *offscreen = led_matrix_create_offscreen_canvas(...); + * led_canvas_set_pixel(offscreen, ...); // not shown until swap-on-vsync + * offscreen = led_matrix_swap_on_vsync(matrix, offscreen); + * // The returned buffer, assigned to offscreen, is now the inactive buffer + * // fill, then swap again. + */ +struct LedCanvas *led_matrix_swap_on_vsync(struct RGBLedMatrix *matrix, + struct LedCanvas *canvas); + +uint8_t led_matrix_get_brightness(struct RGBLedMatrix *matrix); +void led_matrix_set_brightness(struct RGBLedMatrix *matrix, uint8_t brightness); + +// Utility function: set an image from the given buffer containing pixels. +// +// Draw image of size "image_width" and "image_height" from pixel at +// canvas-offset "canvas_offset_x", "canvas_offset_y". Image will be shown +// cropped on the edges if needed. +// +// The canvas offset can be negative, i.e. the image start can be shifted +// outside the image frame on the left/top edge. +// +// The buffer needs to be organized as rows with columns of three bytes +// organized as rgb or bgr. Thus the size of the buffer needs to be exactly +// (3 * image_width * image_height) bytes. +// +// The "image_buffer" parameters contains the data, "buffer_size_bytes" the +// size in bytes. +// +// If "is_bgr" is 1, the buffer is treated as BGR pixel arrangement instead +// of RGB with is_bgr = 0. +void set_image(struct LedCanvas *c, int canvas_offset_x, int canvas_offset_y, + const uint8_t *image_buffer, size_t buffer_size_bytes, + int image_width, int image_height, + char is_bgr); + +// Load a font given a path to a font file containing a bdf font. +struct LedFont *load_font(const char *bdf_font_file); + +// Read the baseline of a font +int baseline_font(struct LedFont *font); + +// Read the height of a font +int height_font(struct LedFont *font); + +// Get the width of a specific codepoint in the given font +int character_width_font(struct LedFont *font, uint32_t unicode_codepoint); + +// Creates an outline font based on an existing font instance +struct LedFont *create_outline_font(struct LedFont *font); + +// Delete a font originally created from load_font. +void delete_font(struct LedFont *font); + +int draw_text(struct LedCanvas *c, struct LedFont *font, int x, int y, + uint8_t r, uint8_t g, uint8_t b, + const char *utf8_text, int kerning_offset); + +int vertical_draw_text(struct LedCanvas *c, struct LedFont *font, int x, int y, + uint8_t r, uint8_t g, uint8_t b, + const char *utf8_text, int kerning_offset); + +void draw_circle(struct LedCanvas *c, int x, int y, int radius, + uint8_t r, uint8_t g, uint8_t b); + +void draw_line(struct LedCanvas *c, int x0, int y0, int x1, int y1, + uint8_t r, uint8_t g, uint8_t b); + +#ifdef __cplusplus +} // extern C +#endif + +#endif diff --git a/deps/rpi-rgb-led-matrix/include/led-matrix.h b/deps/rpi-rgb-led-matrix/include/led-matrix.h new file mode 100644 index 000000000000..d3b9e6c07b74 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/include/led-matrix.h @@ -0,0 +1,520 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see + +// Controlling 16x32 or 32x32 RGB matrixes via GPIO. It allows daisy chaining +// of a string of these, and also connecting a parallel string on newer +// Raspberry Pis with more GPIO pins available. + +#ifndef RPI_RGBMATRIX_H +#define RPI_RGBMATRIX_H + +#include +#include + +#include +#include + +#include "canvas.h" +#include "thread.h" +#include "pixel-mapper.h" +#include "graphics.h" + +namespace rgb_matrix { +class RGBMatrix; +class FrameCanvas; // Canvas for Double- and Multibuffering +struct RuntimeOptions; + +// The RGB matrix provides the framebuffer and the facilities to constantly +// update the LED matrix. +// +// This implement the Canvas interface that represents the display with +// (led_cols * chained_displays)x(rows * parallel_displays) pixels. +// +// If can do multi-buffering using the CreateFrameCanvas() and SwapOnVSync() +// methods. This is useful for animations and to prevent tearing. +// +// If you arrange the panels in a different way in the physical space, write +// a CanvasTransformer that does coordinate remapping and which should be added +// to the transformers, like with UArrangementTransformer in demo-main.cc. +class RGBMatrix : public Canvas { +public: + // Options to initialize the RGBMatrix. Also see the main README.md for + // detailed descriptions of the command line flags. + struct Options { + Options(); // Creates a default option set. + + // Validate the options and possibly output a message to string. If + // "err" is NULL, outputs validation problems to stderr. + // Returns 'true' if all options look good. + bool Validate(std::string *err) const; + + // Name of the hardware mapping. Something like "regular" or "adafruit-hat" + const char *hardware_mapping; + + // The "rows" are the number + // of rows supported by the display, so 32 or 16. Default: 32. + // Flag: --led-rows + int rows; + + // The "cols" are the number of columns per panel. Typically something + // like 32, but also 64 is possible. Sometimes even 40. + // cols * chain_length is the total length of the display, so you can + // represent a 64 wide display as cols=32, chain=2 or cols=64, chain=1; + // same thing, but more convenient to think of. + // Flag: --led-cols + int cols; + + // The chain_length is the number of displays daisy-chained together + // (output of one connected to input of next). Default: 1 + // Flag: --led-chain + int chain_length; + + // The number of parallel chains connected to the Pi; in old Pis with 26 + // GPIO pins, that is 1, in newer Pis with 40 interfaces pins, that can + // also be 2 or 3. The effective number of pixels in vertical direction is + // then thus rows * parallel. Default: 1 + // Flag: --led-parallel + int parallel; + + // Set PWM bits used for output. Default is 11, but if you only deal with + // limited comic-colors, 1 might be sufficient. Lower require less CPU and + // increases refresh-rate. + // Flag: --led-pwm-bits + int pwm_bits; + + // Change the base time-unit for the on-time in the lowest + // significant bit in nanoseconds. + // Higher numbers provide better quality (more accurate color, less + // ghosting), but have a negative impact on the frame rate. + // Flag: --led-pwm-lsb-nanoseconds + int pwm_lsb_nanoseconds; + + // The lower bits can be time-dithered for higher refresh rate. + // Flag: --led-pwm-dither-bits + int pwm_dither_bits; + + // The initial brightness of the panel in percent. Valid range is 1..100 + // Default: 100 + // Flag: --led-brightness + int brightness; + + // Scan mode: 0=progressive, 1=interlaced. + // Flag: --led-scan-mode + int scan_mode; + + // Default row address type is 0, corresponding to direct setting of the + // row, while row address type 1 is used for panels that only have A/B, + // typically some 64x64 panels + int row_address_type; // Flag --led-row-addr-type + + // Type of multiplexing. 0 = direct, 1 = stripe, 2 = checker,... + // Flag: --led-multiplexing + int multiplexing; + + // Disable the PWM hardware subsystem to create pulses. + // Typically, you don't want to disable hardware pulsing, this is mostly + // for debugging and figuring out if there is interference with the + // sound system. + // This won't do anything if output enable is not connected to GPIO 18 in + // non-standard wirings. + bool disable_hardware_pulsing; // Flag: --led-hardware-pulse + + // Show refresh rate on the terminal for debugging and tweaking purposes. + bool show_refresh_rate; // Flag: --led-show-refresh + + // Some panels have inversed colors. + bool inverse_colors; // Flag: --led-inverse + + // In case the internal sequence of mapping is not "RGB", this contains the + // real mapping. Some panels mix up these colors. String of length three + // which has to contain all characters R, G and B. + const char *led_rgb_sequence; // Flag: --led-rgb-sequence + + // A string describing a sequence of pixel mappers that should be applied + // to this matrix. A semicolon-separated list of pixel-mappers with optional + // parameter. + const char *pixel_mapper_config; // Flag: --led-pixel-mapper + + // Panel type. Typically an empty string or NULL, but some panels need + // a particular initialization sequence, so this is used for that. + // This can be e.g. "FM6126A" for that particular panel type. + const char *panel_type; // Flag: --led-panel-type + + // Limit refresh rate of LED panel. This will help on a loaded system + // to keep a constant refresh rate. <= 0 for no limit. + int limit_refresh_rate_hz; // Flag: --led-limit-refresh + + // Sleep instead of busy wait to free CPU cycles but get slightly less + // accurate frame timing. + bool disable_busy_waiting; // Flag: --led-busy-waiting + }; + + // Factory to create a matrix. Additional functionality includes dropping + // privileges and becoming a daemon. + // Returns NULL, if there was a problem (a message then is written to stderr). + static RGBMatrix *CreateFromOptions(const Options &options, + const RuntimeOptions &runtime_options); + + // A factory that parses your main() commandline flags to read options + // meant to configure the the matrix and returns a freshly allocated matrix. + // + // Optionally, you can pass in option structs with a couple of defaults + // which are used unless overwritten on the command line. + // A matrix is created and returned; also the options structs are + // updated to reflect the values that were used and set on the command line. + // + // If you allow the user to start a daemon with --led-daemon, make sure to + // call this function before you have started any threads, so early on in + // main() (see RuntimeOptions documentation). + // + // Note, the permissions are dropped by default from 'root' to 'daemon', so + // if you are required to stay root after this, disable this option in + // the default RuntimeOptions (set drop_privileges = -1). + // Returns NULL, if there was a problem (a message then is written to stderr). + static RGBMatrix *CreateFromFlags(int *argc, char ***argv, + RGBMatrix::Options *default_options = NULL, + RuntimeOptions *default_runtime_opts = NULL, + bool remove_consumed_flags = true); + + // Stop matrix, delete all resources. + virtual ~RGBMatrix(); + + // -- Canvas interface. These write to the active FrameCanvas + // (see documentation in canvas.h) + // + // Since this is updating the canvas that is currently displayed, this + // might result in tearing. + // Prefer using a FrameCanvas and do double-buffering, see section below. + virtual int width() const; + virtual int height() const; + virtual void SetPixel(int x, int y, + uint8_t red, uint8_t green, uint8_t blue); + virtual void Clear(); + virtual void Fill(uint8_t red, uint8_t green, uint8_t blue); + + // -- Double- and Multibuffering. + + // Create a new buffer to be used for multi-buffering. The returned new + // Buffer implements a Canvas with the same size as this RGBMatrix. + // You can use it to draw off-screen on it, then swap it with the active + // buffer using SwapOnVSync(). That would be classic double-buffering. + // + // You can also create as many FrameCanvas as you like and for instance use + // them to pre-fill scenes of an animation for fast playback later. + // + // The ownership of the created Canvases remains with the RGBMatrix, so you + // don't have to worry about deleting them (but you also don't want to create + // more than needed as this will fill up your memory as they are only deleted + // when the RGBMatrix is deleted). + FrameCanvas *CreateFrameCanvas(); + + // This method waits to the next VSync and swaps the active buffer with the + // supplied buffer. The formerly active buffer is returned. + // + // If you pass in NULL, the active buffer is returned, but it won't be + // replaced with NULL. You can use the NULL-behavior to just wait on + // VSync or to retrieve the initial buffer when preparing a multi-buffer + // animation. + // + // The optional "framerate_fraction" parameter allows to choose which + // multiple of the global frame-count to use. So it slows down your animation + // to an exact integer fraction of the refresh rate. + // Default is 1, so immediately next available frame. + // (Say you have 140Hz refresh rate, then a value of 5 would give you an + // 28Hz animation, nicely locked to the refresh-rate). + // If you combine this with Options::limit_refresh_rate_hz you can create + // time-correct animations. + FrameCanvas *SwapOnVSync(FrameCanvas *other, unsigned framerate_fraction = 1); + + // -- Setting shape and behavior of matrix. + + // Apply a pixel mapper. This is used to re-map pixels according to some + // scheme implemented by the PixelMapper. Does _not_ take ownership of the + // mapper. Mapper can be NULL, in which case nothing happens. + // Returns a boolean indicating if this was successful. + bool ApplyPixelMapper(const PixelMapper *mapper); + + // Note, there used to be ApplyStaticTransformer(), which has been deprecated + // since 2018 and changed to a compile-time option, then finally removed + // in 2020. Use PixelMapper instead, which is simpler and more intuitive. + + // Set PWM bits used for output. Default is 11, but if you only deal with + // limited comic-colors, 1 might be sufficient. Lower require less CPU and + // increases refresh-rate. + // + // Returns boolean to signify if value was within range. + // + // This sets the PWM bits for the current active FrameCanvas and future + // ones that are created with CreateFrameCanvas(). + bool SetPWMBits(uint8_t value); + uint8_t pwmbits(); // return the pwm-bits of the currently active buffer. + + // Map brightness of output linearly to input with CIE1931 profile. + void set_luminance_correct(bool on); + bool luminance_correct() const; + + // Set brightness in percent for all created FrameCanvas. 1%..100%. + // This will only affect newly set pixels. + void SetBrightness(uint8_t brightness); + uint8_t brightness(); + + //-- GPIO interaction. + // This library uses the GPIO pins to drive the matrix; this is a safe way + // to request the 'remaining' bits to be used for user purposes. + + // Request user readable GPIO bits. + // This function allows you to request pins you'd like to read with + // AwaitInputChange(). + // Only bits that are not already in use for reading or wrtiting + // by the matrix are allowed. + // Input is a bitmap of all the GPIO bits you're interested in; returns all + // the bits that are actually available. + uint64_t RequestInputs(uint64_t all_interested_bits); + + // This function will return whenever the GPIO input pins + // change (pins that are not already in use for output, that is) or the + // timeout is reached. You need to have reserved the inputs with + // matrix->RequestInputs(...) first (e.g. + // matrix->RequestInputs((1<<25)|(1<<24)); + // + // A positive timeout waits the given amount of milliseconds for a change + // (e.g. a button-press) to occur; if there is no change, it will just + // return the last value. + // If you just want to know how the pins are right now, call with zero + // timeout. + // A negative number waits forever and will only return if there is a change. + // + // This function only samples between display refreshes so polling some + // input does not generate flicker and provide a convenient change interface. + // + // Returns the bitmap of all GPIO input pins. + uint64_t AwaitInputChange(int timeout_ms); + + // Request user writable GPIO bits. + // This allows to request a bitmap of GPIO-bits to be used by the user for + // writing. + // Only bits that are not already in use for reading or wrtiting + // by the matrix are allowed. + // Returns the subset bits that are _actually_ available, + uint64_t RequestOutputs(uint64_t output_bits); + + // Set the user-settable bits according to output bits. + void OutputGPIO(uint64_t output_bits); + + // Legacy way to set gpio pins. We're not doing this anymore but need to + // be source-compatible with old calls of the form + // matrix->gpio()->RequestInputs(...) + // + // Don't use, use AwaitInputChange() directly. + RGBMatrix *gpio() __attribute__((deprecated)) { return this; } + + //-- Rarely needed + // Start the refresh thread. + // This is only needed if you chose RuntimeOptions::daemon = -1 (see below), + // otherwise the refresh thread is already started. + bool StartRefresh(); + +private: + class Impl; + + RGBMatrix(Impl *impl) : impl_(impl) {} + Impl *const impl_; +}; + +namespace internal { +class Framebuffer; +} + +class FrameCanvas : public Canvas { +public: + // Set PWM bits used for this Frame. + // Simple comic-colors, 1 might be sufficient (111 RGB, i.e. 8 colors). + // Lower require less CPU. + // Returns boolean to signify if value was within range. + bool SetPWMBits(uint8_t value); + uint8_t pwmbits(); + + // Map brightness of output linearly to input with CIE1931 profile. + void set_luminance_correct(bool on); + bool luminance_correct() const; + + void SetBrightness(uint8_t brightness); + uint8_t brightness(); + + //-- Serialize()/Deserialize() are fast ways to store and re-create a canvas. + + // Provides a pointer to a buffer of the internal representation to + // be copied out for later Deserialize(). + // + // Returns a "data" pointer and the data "len" in the given out-paramters; + // the content can be copied from there by the caller. + // + // Note, the content is not simply RGB, it is the opaque and platform + // specific representation which allows to make deserialization very fast. + // It is also bigger than just RGB; if you want to store it somewhere, + // using compression is a good idea. + void Serialize(const char **data, size_t *len) const; + + // Load data previously stored with Serialize(). Needs to be restored into + // a FrameCanvas with exactly the same settings (rows, chain, transformer,...) + // as serialized. + // Returns 'false' if size is unexpected. + // This method should only be called if FrameCanvas is off-screen. + bool Deserialize(const char *data, size_t len); + + // Copy content from other FrameCanvas owned by the same RGBMatrix. + void CopyFrom(const FrameCanvas &other); + + // -- Canvas interface. + virtual int width() const; + virtual int height() const; + virtual void SetPixel(int x, int y, + uint8_t red, uint8_t green, uint8_t blue); + virtual void SetPixels(int x, int y, int width, int height, + Color *colors); + virtual void Clear(); + virtual void Fill(uint8_t red, uint8_t green, uint8_t blue); + virtual void SubFill(int x, int y, int width, int height, uint8_t red, uint8_t green, uint8_t blue); + +private: + friend class RGBMatrix; + + FrameCanvas(internal::Framebuffer *frame) : frame_(frame){} + virtual ~FrameCanvas(); // Any FrameCanvas is owned by RGBMatrix. + internal::Framebuffer *framebuffer() { return frame_; } + + internal::Framebuffer *const frame_; +}; + +// Runtime options to simplify doing common things for many programs such as +// dropping privileges and becoming a daemon. +struct RuntimeOptions { + RuntimeOptions(); + + int gpio_slowdown; // 0 = no slowdown. Flag: --led-slowdown-gpio + int rp1_pio; // 0 = default RP1 RIO. 1 = RP1 PIO. Flag: --led-rp1-pio + + // ---------- + // If the following options are set to disabled with -1, they are not + // even offered via the command line flags. + // ---------- + + // -1 : don't leave choice of becoming daemon to the command line + // parsing. If set to -1, the --led-daemon option is not offered. + // 0 : do not become a daemon, run in foreground (default value) + // 0 : do not becoma a daemon, run in forgreound (default value) + // 1 : become a daemon, run in background. + // + // If daemon is disabled (= -1), the user has to call + // RGBMatrix::StartRefresh() manually once the matrix is created, to leave + // the decision to become a daemon + // after the call (which requires that no threads have been started yet). + // In the other cases (off or on), the choice is already made, so the + // thread is conveniently already started for you. + int daemon; // -1 disabled. 0=off, 1=on. Flag: --led-daemon + + // Drop privileges from 'root' to drop_priv_user/group once the hardware is + // initialized. + // This is usually a good idea unless you need to stay on elevated privs. + // -1, 0, 1 similar meaning to 'daemon' above. + int drop_privileges; // -1 disabled. 0=off, 1=on. flag: --led-drop-privs + + // By default, the gpio is initialized for you, but if you run on a platform + // not the Raspberry Pi, this will fail. If you don't need to access GPIO + // e.g. you want to just create a stream output (see content-streamer.h), + // set this to false. + bool do_gpio_init; + + // If drop privileges is enabled, this is the user/group we drop privileges + // to. Unless chosen otherwise, the default is "daemon" for user and group. + const char *drop_priv_user; + const char *drop_priv_group; +}; + +// Convenience utility functions to read standard rgb-matrix flags and create +// a RGBMatrix. Commandline flags are something like --led-rows, --led-chain, +// --led-parallel. See output of PrintMatrixFlags() for all available options +// and detailed description in +// https://github.com/hzeller/rpi-rgb-led-matrix#changing-parameters-via-command-line-flags +// +// Example use: +/* +using rgb_matrix::RGBMatrix; +int main(int argc, char **argv) { + RGBMatrix::Options led_options; + rgb_matrix::RuntimeOptions runtime; + + // Set defaults + led_options.chain_length = 3; + led_options.show_refresh_rate = true; + runtime.drop_privileges = 1; + if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, &led_options, &runtime)) { + rgb_matrix::PrintMatrixFlags(stderr); + return 1; + } + + // Do your own command line handling with the remaining flags. + while (getopt()) {...} + + // Looks like we're ready to start + RGBMatrix *matrix = RGBMatrix::CreateFromOptions(led_options, runtime); + if (matrix == NULL) { + return 1; + } + + // .. now use matrix + + delete matrix; // Make sure to delete it in the end to switch off LEDs. + return 0; +} +*/ +// This parses the flags from argv and updates the structs with the parsed-out +// values. Structs can be NULL if you are not interested in it. +// The recognized flags are removed from argv if "remove_consumed_flags" is +// The recognized flags are removed from argv if "remove_consumed_flags" is +// true; this simplifies your command line processing for the remaining options. +// +// Returns 'true' on success, 'false' if there was flag parsing problem. +bool ParseOptionsFromFlags(int *argc, char ***argv, + RGBMatrix::Options *default_options, + RuntimeOptions *rt_options, + bool remove_consumed_flags = true); + +// Show all the available options in a style that can be used in a --help +// output on the command line. +void PrintMatrixFlags(FILE *out, + const RGBMatrix::Options &defaults = RGBMatrix::Options(), + const RuntimeOptions &rt_opt = RuntimeOptions()); + +// Legacy version of RGBMatrix::CreateFromOptions() +inline RGBMatrix *CreateMatrixFromOptions( + const RGBMatrix::Options &options, + const RuntimeOptions &runtime_options) { + return RGBMatrix::CreateFromOptions(options, runtime_options); +} + +// Legacy version of RGBMatrix::CreateFromFlags() +inline RGBMatrix *CreateMatrixFromFlags( + int *argc, char ***argv, + RGBMatrix::Options *default_options = NULL, + RuntimeOptions *default_runtime_opts = NULL, + bool remove_consumed_flags = true) { + return RGBMatrix::CreateFromFlags(argc, argv, + default_options, default_runtime_opts, + remove_consumed_flags); +} + +} // end namespace rgb_matrix +#endif // RPI_RGBMATRIX_H diff --git a/deps/rpi-rgb-led-matrix/include/pixel-mapper.h b/deps/rpi-rgb-led-matrix/include/pixel-mapper.h new file mode 100644 index 000000000000..7fc0b08e366d --- /dev/null +++ b/deps/rpi-rgb-led-matrix/include/pixel-mapper.h @@ -0,0 +1,118 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2018 Henner Zeller +// +// 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. +// +// 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, see +#ifndef RGBMATRIX_PIXEL_MAPPER +#define RGBMATRIX_PIXEL_MAPPER + +#include +#include + +namespace rgb_matrix { + +// A pixel mapper is a way for you to map pixels of LED matrixes to a different +// layout. If you have an implementation of a PixelMapper, you can give it +// to the RGBMatrix::ApplyPixelMapper(), which then presents you a canvas +// that has the new "visible_width", "visible_height". +class PixelMapper { +public: + virtual ~PixelMapper() {} + + // Get the name of this PixelMapper. Each PixelMapper needs to have a name + // so that it can be referred to with command line flags. + virtual const char *GetName() const = 0; + + // Pixel mappers receive the chain and parallel information and + // might receive optional user-parameters, e.g. from command line flags. + // + // This is a single string containing the parameters. + // You can be used from simple scalar parameters, such as the angle for + // the rotate transformer, or more complex parameters that describe a mapping + // of panels for instance. + // Keep it concise (as people will give parameters on the command line) and + // don't use semicolons in your string (as they are + // used to separate pixel mappers on the command line). + // + // For instance, the rotate transformer is invoked like this + // --led-pixel-mapper=rotate:90 + // And the parameter that is passed to SetParameter() is "90". + // + // Returns 'true' if parameter was parsed successfully. + virtual bool SetParameters(int chain, int parallel, + const char *parameter_string) { + return true; + } + + // Given a underlying matrix (width, height), returns the + // visible (width, height) after the mapping. + // E.g. a 90 degree rotation might map matrix=(64, 32) -> visible=(32, 64) + // Some multiplexing matrices will double the height and half the width. + // + // While not technically necessary, one would expect that the number of + // pixels stay the same, so + // matrix_width * matrix_height == (*visible_width) * (*visible_height); + // + // Returns boolean "true" if the mapping can be successfully done with this + // mapper. + virtual bool GetSizeMapping(int matrix_width, int matrix_height, + int *visible_width, int *visible_height) + const = 0; + + // Map where a visible pixel (x,y) is mapped to the underlying matrix (x,y). + // + // To be convienently stateless, the first parameters are the full + // matrix width and height. + // + // So for many multiplexing methods this means to map a panel to a double + // length and half height panel (32x16 -> 64x8). + // The logic_x, logic_y are output parameters and guaranteed not to be + // nullptr. + virtual void MapVisibleToMatrix(int matrix_width, int matrix_height, + int visible_x, int visible_y, + int *matrix_x, int *matrix_y) const = 0; + + virtual bool MapMatrixToVisible(int matrix_width, int matrix_height, + int matrix_x, int matrix_y, + int *visible_x, int *visible_y) const { return false; }; + enum MappingType { + VisibleToMatrix, MatrixToVisible + }; + virtual MappingType GetMappingType() const { return VisibleToMatrix; } +}; + +// This is a place to register PixelMappers globally. If you register your +// PixelMapper before calling RGBMatrix::CreateFromFlags(), the named +// PixelMapper is available in the --led-pixel-mapper options. +// +// Note, you don't _have_ to register your mapper, you can always call +// RGBMatrix::ApplyPixelMapper() directly. Registering is for convenience and +// commandline-flag support. +// +// There are a few standard mappers registered by default. +void RegisterPixelMapper(PixelMapper *mapper); + +// Get a list of the names of available pixel mappers. +std::vector GetAvailablePixelMappers(); + +// Given a name (e.g. "rotate") and a parameter (e.g. "90"), return the +// parametrized PixelMapper with that name. Returns NULL if mapper +// can not be found or parameter is invalid. +// Ownership of the returned object is _NOT_ transferred to the caller. +// Current available mappers are "U-mapper" and "Rotate". The "Rotate" +// gets a parameter denoting the angle. +const PixelMapper *FindPixelMapper(const char *name, + int chain, int parallel, + const char *parameter = NULL); +} // namespace rgb_matrix + +#endif // RGBMATRIX_PIXEL_MAPPER diff --git a/deps/rpi-rgb-led-matrix/include/thread.h b/deps/rpi-rgb-led-matrix/include/thread.h new file mode 100644 index 000000000000..1f1679536cb8 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/include/thread.h @@ -0,0 +1,86 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see + +#ifndef RPI_THREAD_H +#define RPI_THREAD_H + +#include +#include + +namespace rgb_matrix { +// Simple thread abstraction. +class Thread { +public: + Thread(); + + // The destructor waits for Run() to return so make sure it does. + virtual ~Thread(); + + // Wait for the Run() method to return. + void WaitStopped(); + + // Start thread. If realtime_priority is > 0, then this will be a + // thread with SCHED_FIFO and the given priority. + // If cpu_affinity is set !=, chooses the given bitmask of CPUs + // this thread should have an affinity to. + // On a Raspberry Pi 1, this doesn't matter, as there is only one core, + // Raspberry Pi 2 can has 4 cores, so any combination of (1<<0) .. (1<<3) is + // valid. + virtual void Start(int realtime_priority = 0, uint32_t cpu_affinity_mask = 0); + + // Override this to do the work. + // + // This will be called in a thread once Start() has been called. You typically + // will have an endless loop doing stuff. + // + // It is a good idea to provide a way to communicate to the thread that + // it should stop (see ThreadedCanvasManipulator for an example) + virtual void Run() = 0; + +private: + static void *PthreadCallRun(void *tobject); + bool started_; + pthread_t thread_; +}; + +// Non-recursive Mutex. +class Mutex { +public: + Mutex() { pthread_mutex_init(&mutex_, NULL); } + ~Mutex() { pthread_mutex_destroy(&mutex_); } + void Lock() { pthread_mutex_lock(&mutex_); } + void Unlock() { pthread_mutex_unlock(&mutex_); } + + // Wait on condition. If "timeout_ms" is < 0, it waits forever, otherwise + // until timeout is reached. + // Returns 'true' if condition is met, 'false', if wait timed out. + bool WaitOn(pthread_cond_t *cond, long timeout_ms = -1); + +private: + pthread_mutex_t mutex_; +}; + +// Useful RAII wrapper around mutex. +class MutexLock { +public: + MutexLock(Mutex *m) : mutex_(m) { mutex_->Lock(); } + ~MutexLock() { mutex_->Unlock(); } +private: + Mutex *const mutex_; +}; + +} // end namespace rgb_matrix + +#endif // RPI_THREAD_H diff --git a/deps/rpi-rgb-led-matrix/include/threaded-canvas-manipulator.h b/deps/rpi-rgb-led-matrix/include/threaded-canvas-manipulator.h new file mode 100644 index 000000000000..c213ec68764d --- /dev/null +++ b/deps/rpi-rgb-led-matrix/include/threaded-canvas-manipulator.h @@ -0,0 +1,103 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2014 Henner Zeller +// +// 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. +// +// 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, see + +// Utility base class for continuously updating the canvas. + +// Note: considering removing this, as real applications likely have something +// similar, but this might not be quite usable. +// Since it is just a few lines of code, it is probably better +// implemented in the application for readability. +// +// So for simplicity of the API, consider ThreadedCanvasManipulator deprecated. + +#ifndef RPI_THREADED_CANVAS_MANIPULATOR_H +#define RPI_THREADED_CANVAS_MANIPULATOR_H + +#include "thread.h" +#include "canvas.h" + +namespace rgb_matrix { +// +// Typically, your programs will create a canvas and then updating the image +// in a loop. If you want to do stuff in parallel, then this utility class +// helps you doing that. Also a demo for how to use the Thread class. +// +// Extend it, then just implement Run(). Example: +/* + class MyCrazyDemo : public ThreadedCanvasManipulator { + public: + MyCrazyDemo(Canvas *canvas) : ThreadedCanvasManipulator(canvas) {} + virtual void Run() { + unsigned char c; + while (running()) { + // Calculate the next frame. + c++; + for (int x = 0; x < canvas()->width(); ++x) { + for (int y = 0; y < canvas()->height(); ++y) { + canvas()->SetPixel(x, y, c, c, c); + } + } + usleep(15 * 1000); + } + } + }; + + // Later, in your main method. + RGBMatrix *matrix = RGBMatrix::CreateFromOptions(...); + MyCrazyDemo *demo = new MyCrazyDemo(matrix); + demo->Start(); // Start doing things. + // This now runs in the background, you can do other things here, + // e.g. acquiring new data or simply wait. But for waiting, you wouldn't + // need a thread in the first place. + demo->Stop(); + delete demo; +*/ +class ThreadedCanvasManipulator : public Thread { +public: + ThreadedCanvasManipulator(Canvas *m) : running_(false), canvas_(m) {} + virtual ~ThreadedCanvasManipulator() { Stop(); } + + virtual void Start(int realtime_priority=0, uint32_t affinity_mask=0) { + { + MutexLock l(&mutex_); + running_ = true; + } + Thread::Start(realtime_priority, affinity_mask); + } + + // Stop the thread at the next possible time Run() checks the running_ flag. + void Stop() { + MutexLock l(&mutex_); + running_ = false; + } + + // Implement this and run while running() returns true. + virtual void Run() = 0; + +protected: + inline Canvas *canvas() { return canvas_; } + inline bool running() { + MutexLock l(&mutex_); + return running_; + } + +private: + Mutex mutex_; + bool running_; + Canvas *const canvas_; +}; +} // namespace rgb_matrix + +#endif // RPI_THREADED_CANVAS_MANIPULATOR_H diff --git a/deps/rpi-rgb-led-matrix/lib/.gitignore b/deps/rpi-rgb-led-matrix/lib/.gitignore new file mode 100644 index 000000000000..80c47cb536dc --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/.gitignore @@ -0,0 +1,3 @@ +compiler-flags +librgbmatrix.a +librgbmatrix.so.1 diff --git a/deps/rpi-rgb-led-matrix/lib/CMakeLists.txt b/deps/rpi-rgb-led-matrix/lib/CMakeLists.txt new file mode 100644 index 000000000000..fc70543bb53f --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/CMakeLists.txt @@ -0,0 +1,119 @@ +# CMake for rpi-rgb-led-matrix core library + +#cmake_minimum_required(VERSION 3.22) +#project( +# rgbmatrix +# VERSION "0.0.1" +# LANGUAGES CXX) +#set(CMAKE_CXX_STANDARD 17) +#set(CMAKE_CXX_STANDARD_REQUIRED ON) +#include(CMakePrintHelpers) # so we can print cmake variable values + +# cmake_print_variables(CMAKE_CURRENT_SOURCE_DIR) + +set(RGBMATRIX_RP1_PIO_VENDOR_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/rp1/rp1_pio_vendor) + +# Define the RGBMATRIX headers. +set(RGBMATRIX_HEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/../include/canvas.h + ${CMAKE_CURRENT_SOURCE_DIR}/../include/content-streamer.h + ${CMAKE_CURRENT_SOURCE_DIR}/../include/graphics.h + ${CMAKE_CURRENT_SOURCE_DIR}/../include/led-matrix.h + ${CMAKE_CURRENT_SOURCE_DIR}/../include/led-matrix-c.h + ${CMAKE_CURRENT_SOURCE_DIR}/../include/pixel-mapper.h + ${CMAKE_CURRENT_SOURCE_DIR}/../include/thread.h + ${CMAKE_CURRENT_SOURCE_DIR}/../include/threaded-canvas-manipulator.h +) +cmake_print_variables(RGBMATRIX_HEADERS) + +# Define the RGBMATRIX source files +set(RGBMATRIX_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/bdf-font.cc + ${CMAKE_CURRENT_SOURCE_DIR}/content-streamer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/framebuffer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/gpio.cc + ${CMAKE_CURRENT_SOURCE_DIR}/graphics.cc + ${CMAKE_CURRENT_SOURCE_DIR}/hardware-mapping.c + ${CMAKE_CURRENT_SOURCE_DIR}/led-matrix.cc + ${CMAKE_CURRENT_SOURCE_DIR}/led-matrix-c.cc + ${CMAKE_CURRENT_SOURCE_DIR}/multiplex-mappers.cc + ${CMAKE_CURRENT_SOURCE_DIR}/options-initialize.cc + ${CMAKE_CURRENT_SOURCE_DIR}/pixel-mapper.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rp1/rp1_pio_backend.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rp1/rp1_pio_support.c + ${CMAKE_CURRENT_SOURCE_DIR}/rp1/rp1_rio_backend.cc + ${CMAKE_CURRENT_SOURCE_DIR}/thread.cc +) +cmake_print_variables(RGBMATRIX_SOURCES) + +# Declare the library +if(PROJECT_IS_TOP_LEVEL) + add_library(rgbmatrix SHARED) +endif() + +# Add the sources +target_sources(rgbmatrix PRIVATE ${RGBMATRIX_SOURCES}) + +# Add include directories +target_include_directories(rgbmatrix PUBLIC + $ ) +target_include_directories(rgbmatrix PRIVATE + ${RGBMATRIX_RP1_PIO_VENDOR_DIR}/piolib/include + ${RGBMATRIX_RP1_PIO_VENDOR_DIR}/include +) + +# Install the library. +#install( +# TARGETS rgbmatrix +# EXPORT rgbmatrixTargets +# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +#) +install( + TARGETS rgbmatrix + EXPORT rgbmatrixTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) +#install( +# TARGETS rgbmatrix +# EXPORT rgbmatrixTargets +# FILE_SET HEADERS +# LIBRARY DESTINATION lib +# ARCHIVE DESTINATION lib +# RUNTIME DESTINATION bin +# INCLUDES DESTINATION include +#) +cmake_print_variables(${rgbmatrixTargets}) +#### +# +# cmake install files, so other packages can find this library. +# +#### +set(RGBMATRIX_CMAKE_PACKAGE_INSTALL_SUBDIR "share/rgbmatrix/cmake") + +#set(SYSCONFIG_INSTALL_DIR ${CMAKE_INSTALL_SYSCONFDIR} +# CACHE PATH "Location of configuration files" ) +install( + EXPORT rgbmatrixTargets # this name before Targets should match the name of the variable set above in a previous install call + NAMESPACE rgbmatrix:: + DESTINATION ${RGBMATRIX_CMAKE_PACKAGE_INSTALL_SUBDIR}) + +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + rgbmatrixConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) + +set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}) #set so we can use in configure_package_config_file() +set(LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}) # ditto + +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/rgbmatrixConfig.cmake.in" rgbmatrixConfig.cmake + PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR LIB_INSTALL_DIR + INSTALL_DESTINATION ${RGBMATRIX_CMAKE_PACKAGE_INSTALL_SUBDIR} + ) + +install(FILES "${PROJECT_BINARY_DIR}/rgbmatrixConfig.cmake" + "${PROJECT_BINARY_DIR}/rgbmatrixConfigVersion.cmake" + DESTINATION ${RGBMATRIX_CMAKE_PACKAGE_INSTALL_SUBDIR}) diff --git a/deps/rpi-rgb-led-matrix/lib/Makefile b/deps/rpi-rgb-led-matrix/lib/Makefile new file mode 100644 index 000000000000..75290192b479 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/Makefile @@ -0,0 +1,209 @@ +# Creating RGB matrix library +# When you link this library with your binary, you need to add -lrt -lm -lpthread +# So +# -lrgbmatrix +## +include ../config.mk + +OBJECTS=gpio.o led-matrix.o options-initialize.o framebuffer.o \ + thread.o bdf-font.o graphics.o led-matrix-c.o hardware-mapping.o \ + pixel-mapper.o multiplex-mappers.o \ + content-streamer.o content-streamer-c.o \ + rp1/rp1_pio_backend.o rp1/rp1_pio_support.o rp1/rp1_rio_backend.o + +TARGET=librgbmatrix + +### +# After you change any of the following DEFINES, make sure to 'make' again. +# +# ########### NOTE ########### +# all of these options can now can be set programmatically and +# via command line flags as well. No real need to change them in the Makefile. +# (So be prepared for these to be removed at some point) +### + +# There are several different pinouts for various breakout boards that uses +# this library. If you are using the described pinout in the toplevel README.md +# or the standard active-3 breakout board, then 'regular' is the one you'd like +# to use. +# +# Adafruit also made a breakout board, if you want to use that, choose +# 'adafruit-hat' +# +# These are the choices +# regular # Following this project wiring and using these PCBs +# adafruit-hat # If you have a RGB matrix HAT from Adafruit +# adafruit-hat-pwm # If you have an Adafruit HAT with PWM hardware mod. +# regular-pi1 # If you have an old Pi1 and regular didn't work. +# classic # (deprecated) Classic Pi1/2/. Not used anymore. +# classic-pi1 # (deprecated) Classic pinout on Rasperry Pi 1 +HARDWARE_DESC?=regular + +# If you see that your display is inverse, you might have a matrix variant +# has uses inverse logic for the RGB bits. In that case: uncomment this. +# Flag: --led-inverse +#DEFINES+=-DINVERSE_RGB_DISPLAY_COLORS + +# For curiosity reasons and while tweaking values for LSB_PWM_NANOSECONDS, +# uncomment to see refresh rate in terminal. +# Flag: --led-show-refresh +#DEFINES+=-DSHOW_REFRESH_RATE + +# For low refresh rates below 100Hz (e.g. a lot of panels), the eye will notice +# some flicker. With this option enabled, the refreshed lines are interleaved, +# so it is less noticeable. But looks less pleasant with fast eye movements. +# Flag: --led-scan-mode=1 +#DEFINES+=-DRGB_SCAN_INTERLACED=1 + +# The signal can be too fast for some LED panels, in particular with newer +# (faster) Raspberry Pi 2s - in that case, the LED matrix only shows garbage. +# This allows to slow down the GPIO for these cases. +# +# Set to 1 for RPi2 or RPi3 (default below), because they are typically +# faster than the panels can digest. +# +# Set to 0 (or comment out) for RPi1, that are slow enough. +# +# Sometimes, you even have to give RGB_SLOWDOWN_GPIO=2 or even 3 for +# particularly slow panels or bad signal cable situations. If that happens, you +# typically should double check cables and add TTL level converter if you +# haven't. +# Flag: --led-slowdown-gpio +#DEFINES+=-DRGB_SLOWDOWN_GPIO=1 + +# This allows to change the base time-unit for the on-time in the lowest +# significant bit in nanoseconds. +# Higher numbers provide better quality (more accurate color, less ghosting), +# but have a negative impact on the frame rate. +# +# For the same frame-rate, displays with higher multiplexing (e.g. 1:16 or 1:32) +# require lower values. +# +# Good values for full-color display (PWM=11) are somewhere between 100 and 300. +# +# If you you use reduced bit color (e.g. PWM=1 for 8 colors like for text), +# then higher values might be good to minimize ghosting (and you can afford +# that, because lower PWM values result in higher frame-rates). +# +# How to decide ? Just leave the default if things are fine. If you see +# ghosting in high-contrast applications (e.g. text), increase the value. +# If you want to tweak, watch the framerate (-DSHOW_FRAME_RATE) while playing +# with this number and the PWM values. +# Flag: --led-pwm-lsb-nanoseconds +#DEFINES+=-DLSB_PWM_NANOSECONDS=130 + +# This is to debug problems with the hardware pulse generation. The PWM hardware +# module is also used by Raspberry Pi sound system, so there might be +# interference. Note, you typically don't want the hardware pulses disabled, as +# the image will have visible brightness glitches; but for debugging, this is +# a good choice. +# Flag: --led-no-hardware-pulses +#DEFINES+=-DDISABLE_HARDWARE_PULSES + +# This allows to fix the refresh rate to a particular refresh time in +# microseconds. +# +# This can be used to mitigate some situations in which you have a rare +# faint flicker, which can happen due to hardware events (network access) +# or other situations such as other IO or heavy memory access by other +# processes (all of which seem to break the isolation we request from the +# kernel. You did set isolcpus=3 right ?) +# You trade a slightly slower refresh rate and display brightness for less +# visible flicker situations. +# +# For this to calibrate, run your program for a while with --led-show-refresh +# and watch the line that shows the refresh time and the maximum microseconds +# for a frame observed. The maximum number is updated whenever the frame +# refresh take a little bit longer. So wait a while until that value doesn't +# change anymore (at least a minute, so that you catch tasks that happen once +# a minute). Some value might read e.g. +# 204.6Hz max: 5133usec +# Now take this maximum value you see there (here: 5133) and put in +# this define (don't forget to remove the # in front). +# +# The refresh rate will now be adapted to always have this amount of time +# between frames, so faster refreshes will be slowed down, but the occasional +# delayed frame will fit into the time-window as well, thus reducing visible +# brightness fluctuations. +# +# You can play with value a little and reduce until you find a good balance +# between refresh rate (which is reduce the higher this value is) and +# flicker suppression (which is better with higher values). +# Flag: --led-limit-refresh +#DEFINES+=-DFIXED_FRAME_MICROSECONDS=5000 + +# When limiting refrash rate, a CPU core is busy waiting to get accurate +# timing. On single board systems, this results in an unresponsive system. +# By disabling busy waiting, CPU cycles are freed up, leading to a more +# responsive system at the cost of slightly less accurate frame timing. +# Flag: --led-no-busy-waiting +#DEFINES+=-DDISABLE_BUSY_WAITING + +# Enable wide 64 bit GPIO offered with the compute module. +# This will use more memory to internally represent the frame buffer, so +# caches can't be utilized as much. +# So only switch this on if you really use the compute module and use more +# than 3 parallel chains. +# (this is untested right now, waiting for hardware to arrive for testing) +#DEFINES+=-DENABLE_WIDE_GPIO_COMPUTE_MODULE + +# ---- Pinout options for hardware variants; usually no change needed here ---- + +# Uncomment if you want to use the Adafruit HAT with stable PWM timings. +# The newer version of this library allows for much more stable (less flicker) +# output, but it does not work with the Adafruit HAT unless you do a +# simple hardware hack on them: +# connect GPIO 4 (old OE) with 18 (the new OE); there are +# convenient solder holes labeled 4 and 18 on the Adafruit HAT, pretty +# close together. +# Then you can set the flag --led-gpio-mapping=adafruit-hat-pwm +# .. or uncomment the following line. +#HARDWARE_DESC=adafruit-hat-pwm + +# Typically, a Hub75 panel is split in two half displays, so that a 1:16 +# multiplexing actually multiplexes over two half displays and gives 32 lines. +# There are some other displays out there that you might experiment with +# that are internally wired to only have one sub-panel. In that case you might +# want to try this define to get a more reasonable canvas mapping. +# This option is typically _not_ needed, only use when you attempt to connect +# some oddball old (typically one-colored) display, such as Hub12. +#DEFINES+=-DONLY_SINGLE_SUB_PANEL + +# If someone gives additional values on the make commandline e.g. +# make USER_DEFINES="-DSHOW_REFRESH_RATE" +DEFINES+=$(USER_DEFINES) + +DEFINES+=-DDEFAULT_HARDWARE='"$(HARDWARE_DESC)"' +INCDIR=../include +RP1_PIO_VENDOR_DIR=./rp1/rp1_pio_vendor +RP1_PIO_INCLUDES=-I$(RP1_PIO_VENDOR_DIR)/piolib/include -I$(RP1_PIO_VENDOR_DIR)/include + +CFLAGS=-W -Wall -Wextra -Wno-unused-parameter -O3 $(CPU_ARCH_FLAGS) $(LTO_FLAGS) -g -fPIC $(DEFINES) $(RP1_PIO_INCLUDES) +CXXFLAGS=$(CFLAGS) -fno-exceptions -std=c++11 + +all : $(TARGET).a $(TARGET).so.1 + +$(TARGET).a : $(OBJECTS) + $(AR) rcs $@ $^ + +$(TARGET).so.1 : $(OBJECTS) + $(CXX) -shared $(LTO_FLAGS) -Wl,-soname,$@ -o $@ $^ -lpthread -lrt -lm -lpthread + +led-matrix.o: led-matrix.cc $(INCDIR)/led-matrix.h +thread.o : thread.cc $(INCDIR)/thread.h +framebuffer.o: framebuffer.cc framebuffer-internal.h +graphics.o: graphics.cc utf8-internal.h + +%.o : %.cc compiler-flags + $(CXX) -I$(INCDIR) $(CXXFLAGS) -c -o $@ $< + +%.o : %.c compiler-flags + $(CC) -I$(INCDIR) $(CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJECTS) $(TARGET).a $(TARGET).so.1 + +compiler-flags: FORCE + @echo '$(CXX) $(CXXFLAGS)' | cmp -s - $@ || echo '$(CXX) $(CXXFLAGS)' > $@ + +.PHONY: FORCE diff --git a/deps/rpi-rgb-led-matrix/lib/README.md b/deps/rpi-rgb-led-matrix/lib/README.md new file mode 100644 index 000000000000..cfa813215c13 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/README.md @@ -0,0 +1,381 @@ +Remapping coordinates (pixels within panels and assigning pixel order across panels) +------------------------------------------------------------------------------------ +Some panels do not even output pixels in the natural left to right, up to down order. +For those, see multipler lower down on this page. + +For now, let's focus on how panels are chained. +You might choose a different physical layout than the wiring provides. +The library by default will do this the horizontal way +``` +chain 1 - P1 - P2 - P3 +chain 2 - P1 - P2 - P3 +chain 3 - P1 - P2 - P3 +``` + +Once you go past 4 or 5 panels wide, you may want to have a layout like this. +For that, you want U-mapper. +``` +chain 1 - P1 - P2 - P3 + P6 - P5 - P4 +chain 2 - P1 - P2 - P3 + P6 - P5 - P4 +chain 3 - P1 - P2 - P3 + P6 - P5 - P4 +``` + +Remember that you can always rotate the display in software, so your main goal is to +have 2 or 3 channels in any direction. This means indeed the channels can go down +instead with V-mapper and V-mapper:Z (see below for more). Note that in that case the +pixels go up. +``` + P3 + P2 + P1 +chain 1 chain2 chain3 +``` + +But what if you have a layout that really need 4 channels, like Mark Estes' monster 52 +panel display (made with 32x16 panels) +``` +P01 P02 ... P13 +P14 P15 ... P26 +P27 P28 ... P39 +P40 P41 ... P52 +``` +The problem here is that V-Mapper does not support U, and coming back down for a 2nd row +but when you are stuck, you should still consider V-Mapper and connecting panels that are +side by side as one logical panel. + +So let's rotate that (remember physical orientation doesn't matter, you can add Rotate:90 at the end) +``` +P40-P27 P14-P01 + +P50-P37 +^ +P51-P38 + ^ +P52-P39 P26-P13 +^ ^ +chain1 chain2 +``` +Now, V-Mapper:Z does not need to know that you have 2 adjacent panels connected on each +horizontal line, what you do is tell it +``` +-led-rows=16 --led-cols=64 --led-pixel-mapper=V-Mapper:Z,Rotate:90 +``` +The important bit is your 32x16 panels become some single 64x16 horizontal panels as far +as the library is concerned and count as a single panel for the V configuration. +That way, you can get the layout you wanted. + +This shows how to turn 32x16 panels into 64x16 panels usable as single vertical +panels for Vmapper:Z. Note that this is not visible in the picture, every other +level the panels are upside down to allow for shorter ribbon cables in between. +Note that you can also use Vmapper, keep all the panels in the same orientation +and use longer ribbon cables between the panels. +image + + +### Standard mappers +There is an option `--led-pixel-mapper` that allows you to choose between +some re-mapping options, and also programmatic ways to do so. + +#### U-mapper (U-shape connection) +Say you have 4 displays with 32x32 and only a single output +like with a Raspberry Pi 1 or the Adafruit HAT -- if we chain +them, we get a display 32 pixel high, (4*32)=128 pixel long. If we arrange +the boards in a U-shape so that they form a square, we get a logical display +of 64x64 pixels: + + + +In action: +[![PixelPusher video][pp-vid]](http://youtu.be/ZglGuMaKvpY) + +``` +So the following chain (Viewed looking at the LED-side of the panels) + [<][<][<][<] }- Raspbery Pi connector + +is arranged in this U-shape (on its side) + [<][<] }----- Raspberry Pi connector + [>][>] +``` + +Now we need to internally map pixels the pixels so that the 'folded' 128x32 +screen behaves like a 64x64 screen. + +There is a pixel-mapper that can help with this "U-Arrangement", you choose +it with `--led-pixel-mapper=U-mapper`. So in this particular case, + +``` + ./demo --led-chain=4 --led-pixel-mapper="U-mapper" +``` + +This works for longer and more than one chain as well. Here an arrangement with +two chains with 8 panels each + +``` + [<][<][<][<] }--- Pi connector #1 + [>][>][>][>] + [<][<][<][<] }--- Pi connector #2 + [>][>][>][>] +``` + +(`--led-chain=8 --led-parallel=2 --led-pixel-mapper="U-mapper"`). + +#### V-mapper and Vmapper:Z (Vertical arrangement) + +By default, when you add panels on a chain, they are added horizontally. +If you have 2 panels of 64x32, you get 128x32. +The V-mapper allows the stacking to be vertical and not horizontal and +get the 64x64 you might want. + +By default, all the panels are correct side up, and you need more cable length +as you need to cross back to the start of the next panel. +If you wish to use shorter cables, you can add use Vmapper:Z which will give +you serpentine cabling and every other panel will be upside down (see below +for an example). + +It is compatible with parallel chains, so you can have multiple stacks +of panels all building a coherent overall display. + +Here an example with 3 chains of 4 panels (128x64) for a total of about +98k display pixels. + +``` + ./demo --led-rows=64 --led-cols=128 --led-chain=4 -led-parallel=3 --led-pixel-mapper=V-mapper -D0 +``` + +Viewed looking the LED-side of the panels: + +``` + Vmapper Vmapper:Z + + [O < I] [O < I] [O < I] [I > O] [I > O] [I > O] + ,---^ ,---^ ,---^ ^ ^ ^ + [O < I] [O < I] [O < I] [O < I] [O < I] [O < I] + ,---^ ,---^ ,---^ ^ ^ ^ + [O < I] [O < I] [O < I] [I > O] [I > O] [I > O] + ,---^ ,---^ ,---^ ^ ^ ^ + [O < I] [O < I] [O < I] [O < I] [O < I] [O < I] + ^ ^ ^ ^ ^ ^ + #1 #2 #3 #1 #2 #3 + Pi connector (three parallel chains of len 4) +``` + + (This is also a good time to notice that 384x256 with 12 128x64 panels, is probably an +upper limit of what you can reasonably output without having an unusable fresh +rate (Try these options to help: --led-pwm-bits=7 --led-pwm-dither-bits=1 and get about 100Hz)). + +This shows the wiring of a 3x5 Vmapper:Z array built by Marc MERLIN, using 15x 64x32 panels: +![Vmapper_Z_192x160_3x5.jpg](../img/Vmapper_Z_192x160_3x5.jpg) +With --led-pwm-bits=7 --led-pwm-dither-bits=1, it gets a better 300Hz refresh +but only offers around 31K pixels instead of 98K pixels in the previous example. + +Please note that Vmapper can also be used to improve the refresh rate of a long +display even if it is only one panel high (e.g. for a text running output) by +splitting the load into multiple parallel chains. + +``` + + [O < I] [O < I] [O < I] + ^ ^ ^ + #1 #2 #3 Pi connector (three parallel chains of len 1) +``` + +#### Rotate + +The "Rotate" mapper allows you to rotate your screen. It takes an angle +as parameter after a colon: + +``` + ./demo --led-pixel-mapper="Rotate:90" +``` + +#### Mirror + +The 'Mirror' mapper allows to mirror the output horizontally or vertically. +Without parameter, it mirrors horizontally. The parameter is a single character +'H' or 'V' for horizontal or vertical mirroring. + +``` + ./demo --led-pixel-mapper="Mirror:H" +``` + +#### StackToRow + +The `StackToRow` mapper allows parallel strands to subsequently be mapped end-to-end. + +After using a combination of the other mappers, you may end up with a virtual +layout similar to the following, where x and y increase to the right and down: +``` +[ STRAND #1 ] +[ STRAND #2 ] +[ STRAND #3 ] +[ STRAND #4 ] +``` +The mapper can then be used to form the final end-to-end arrangement. + +The standard usage (`StackToRow`) is mapped to: +``` +[ STRAND #1 ][ STRAND #2 ][ STRAND #3 ][ STRAND #4 ] +``` + +The serpentine usage (`StackToRow:Z`) is mapped to: +``` +[ STRAND #1 ][ 2# ꓷNꓯꓤꓕS ][ STRAND #3 ][ ᔭ# ꓷNꓯꓤꓕS ] +``` + +The flip usage (`StackToRow:F`) is mapped to: +``` +[ STRAND #1 ][ STRAND #2 ][ Ɛ# ꓷNꓯꓤꓕS ][ ᔭ# ꓷNꓯꓤꓕS ] +``` + +The modifiers can be combined if necessary. + +##### Example layout + +Using 64x32px panels, the following arrangement is a 64x256px display +(visualized from the LED-side): +``` + ⌈I⌉\ ⌈I⌉\ ⌈I⌉\ ⌈I⌉<#1 | ⌈O⌉\ ⌈O⌉\ ⌈O⌉\ ⌈O⌉ + ⌊O⌋ \⌊O⌋ \⌊O⌋ \⌊O⌋ | #2>⌊I⌋ \⌊I⌋ \⌊I⌋ \⌊I⌋ +``` + +This layout can be configured with: +``` +V-mapper;Rotate:90;StackToRow:F;Rotate:180 +``` +The remap mapper can be used to describe this layout: +``` +Remap:256,64|96,0w|64,0w|32,0w|0,0w|128,0e|160,0e|192,0e|224,0e +``` + +#### Combining Mappers + +You can chain multiple mappers in the configuration, by separating them +with a semicolon. The mappers are applied in the sequence you give them, so +if you want to arrange a couple of panels with the U-arrangement, and then +rotate the resulting screen, use + +``` + ./demo --led-chain=8 --led-parallel=3 --led-pixel-mapper="U-mapper;Rotate:90" +``` + +Here, we first create a 128x192 screen (4 panels wide (`4*32=128`), +with three folded chains (`6*32=192`)) and then rotate it by 90 degrees to +get a 192x128 screen. + +#### Programmatic access + +If you want to choose these mappers programmatically from your program and +not via the flags, you can do this by setting the `pixel_mapper_config` option +in the options struct in C++ or Python. + +``` + options.pixel_mapper_config = "Rotate:90"; +``` + +### Feature remap mapper (experimental) + +Please look at https://github.com/hzeller/rpi-rgb-led-matrix/pull/1478 +This is a placeholder until we have more documentation. + +each segment is mapped to arbitrary position and orientation on canvas + +My use case is two separate displays connected as chains, each with different geometry. But it shall handle almost all possible panel configuration + +Syntax is: +``` +--led-pixel-mapper='Remap:,|,|,' +``` +new_width, new_height - size of created canvas. It can be both larger and smaller than old canvas. +panel0_x, panel0_y - upper-left corner of remapped panel +panel0_orientation - `n`,`s`,`e`,`w` for panel orientation, `x` to discard panel + +Exactly one entry must be specified for each LED panel (chain * parallel entries). +Mapping may be partially outside new canvas (maybe useful for something?) +Unused positions must be discarded (`0,0x`) - useful if chains are of different length +It is possible let some canvas space unused (no panel mapped to it). Writes to this area will be ignored. + +For example: +``` + --led-cols=16 --led-rows=8 --led-chain=5 --led-parallel=2 --led-pixel-mapper='Remap:40,32|0,0e|8,0e|16,0e|24,0e|32,0e|0,16n|16,16n|0,24n|16,24n|0,0x' +``` +First chain is 5 panels in line, top of the panel is pointing right, 40x16 pixels +Second chain is 2x2 square, panels pointing up, 32x16 pixels, top-left is 0,16 in canvas +Last panel on second chain is not used +``` +1 2 3 4 5 +1 2 3 4 5 +6 6 7 7 +8 8 9 9 +``` + +image + +``` +That's how it should work. You need to map all panels (end of chain 3), so you must discard last 6 panels: + --led-pixel-mapper='Remap:192,128|0,0s|0,32s|...|112,32n|112,0n|0,0x|0,0x|0,0x|0,0x|0,0x|0,0x' +``` + +### Writing your own mappers + +If you want to write your own mappers, e.g. if you have a fancy panel +arrangement, you can do so using the API provided. + +In the API, there is an interface to implement, +a [`PixelMapper`](../include/pixel-mapper.h) that allows to program +re-arrangements of pixels in any way. You can plug such an implementation of +a `PixelMapper` into the RGBMatrix to use it: + +``` + bool RGBMatrix::ApplyPixelMapper(const PixelMapper *mapper); +``` + +If you want, you can also register your PixelMapper globally before you +parse the command line options; then this pixel-mapper is automatically +provided in the `--led-pixel-mapper` command line option: + +``` + RegisterPixelMapper(new MyOwnPixelMapper()); + RGBMatrix *matrix = RGBMatrix::CreateFromFlags(...); +``` + +Now your mapper can be used alongside (and combined with) the standard +mappers already there (e.g. "U-mapper" or "Rotate"). Your mapper can have +parameters: In the command-line flag, parameters provided after `:` are passed +as-is to your `SetParameters()` implementation +(e.g. using `--led-pixel-mapper="Rotate:90"`, the `Rotate` mapper +gets a parameter string `"90"` as parameter). + +#### Custom mapper example from barryVDs + +Please see https://github.com/hzeller/rpi-rgb-led-matrix/issues/1732 + +![446766687-ef343583-fcf4-47f8-b784-abc83c458c7f](https://github.com/user-attachments/assets/f337e39b-9264-4e4d-bb97-f54686a6e086) + + + +### Multiplex Mappers + +Sometimes you even need this for the panel itself: In some panels +(typically the 'outdoor panels', often with 1:4 multiplexing) the pixels +are not mapped in a straight-forward way, but in a snake arrangement for +instance. + +There are simplified pixel mappers for this purpose, the +[multiplex mappers](../lib/multiplex-mappers.cc). These are defined there +and then can be accessed via the command line flag `--led-multiplexing=...`. + +If you find that whatever parameter you give to `--led-multiplexing=` doesn't +work, you might need to write your own mapper (extend `MultiplexMapperBase` +and implement the one method `MapSinglePanel()`). Then register them with +the `CreateMultiplexMapperList()` function in that file. When you do this, +this will automatically become available in the `--led-multiplexing=` command +line option in C++ and Python. + +[run-vid]: ../img/running-vid.jpg +[git-submodules]: http://git-scm.com/book/en/Git-Tools-Submodules +[pixelpush]: https://github.com/hzeller/rpi-matrix-pixelpusher +[pp-vid]: ../img/pixelpusher-vid.jpg +[otf2bdf]: https://github.com/jirutka/otf2bdf diff --git a/deps/rpi-rgb-led-matrix/lib/bdf-font.cc b/deps/rpi-rgb-led-matrix/lib/bdf-font.cc new file mode 100644 index 000000000000..4ca419ed14a0 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/bdf-font.cc @@ -0,0 +1,247 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2014 Henner Zeller +// Modified 2024 by WMcD (adding ReadFont/parseLine) +// +// 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. +// +// 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, see + +// Some old g++ installations need this macro to be defined for PRIx64. +#ifndef __STDC_FORMAT_MACROS +# define __STDC_FORMAT_MACROS +#endif +#include + +#include "graphics.h" + +#include +#include +#include +#include + +#include +#include +#include + +// The little question-mark box "�" for unknown code. +static const uint32_t kUnicodeReplacementCodepoint = 0xFFFD; + +namespace rgb_matrix { +// Bitmap for one row. This limits the number of available columns. +// Make wider if running into trouble. +static constexpr int kMaxFontWidth = 196; +typedef std::bitset rowbitmap_t; + +struct Font::Glyph { + int device_width, device_height; + int width, height; + int x_offset, y_offset; + std::vector bitmap; // contains 'height' elements. +}; + +static bool readNibble(char c, uint8_t* val) { + if (c >= '0' && c <= '9') { *val = c - '0'; return true; } + if (c >= 'a' && c <= 'f') { *val = c - 'a' + 0xa; return true; } + if (c >= 'A' && c <= 'F') { *val = c - 'A' + 0xa; return true; } + return false; +} + +static bool parseBitmap(const char *buffer, rowbitmap_t* result) { + // Read the bitmap left-aligned to our buffer. + for (int pos = result->size() - 1; *buffer && pos >= 3; buffer+=1) { + uint8_t val; + if (!readNibble(*buffer, &val)) + break; + (*result)[pos--] = val & 0x8; + (*result)[pos--] = val & 0x4; + (*result)[pos--] = val & 0x2; + (*result)[pos--] = val & 0x1; + } + return true; +} + +Font::Font() : font_height_(-1), base_line_(0) {} +Font::~Font() { + for (CodepointGlyphMap::iterator it = glyphs_.begin(); + it != glyphs_.end(); ++it) { + delete it->second; + } +} + +// TODO: that might not be working for all input files yet. +bool Font::LoadFont(const char *path) { + if (!path || !*path) return false; + FILE *f = fopen(path, "r"); + if (f == NULL) + return false; + uint32_t codepoint; + char buffer[1024]; + Glyph tmp; + Glyph *current_glyph = NULL; + int row = 0; + + while (fgets(buffer, sizeof(buffer), f)) { + parseLine(buffer, current_glyph, codepoint, tmp, row); + } + fclose(f); + return true; +} + +bool Font::ReadFont(const char *font_file_as_string) { + if (!font_file_as_string || !*font_file_as_string) return false; + uint32_t codepoint; + constexpr uint32_t BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + Glyph tmp; + Glyph *current_glyph = NULL; + int row = 0; + + std::istringstream f(font_file_as_string); + std::string line; + while (std::getline(f, line)) { + if (line.length() < BUFFER_SIZE) { + strcpy(buffer, line.data()); + } + else { + return false; + } + + parseLine(buffer, current_glyph, codepoint, tmp, row); + } + return true; +} + + void Font::parseLine(const char* buffer, Glyph* ¤t_glyph, uint32_t &codepoint, Glyph &tmp, int &row) { + int dummy; + + if (sscanf(buffer, "FONTBOUNDINGBOX %d %d %d %d", + &dummy, &font_height_, &dummy, &base_line_) == 4) { + base_line_ += font_height_; + } + else if (sscanf(buffer, "ENCODING %ud", &codepoint) == 1) { + // parsed. + } + else if (sscanf(buffer, "DWIDTH %d %d", &tmp.device_width, &tmp.device_height + ) == 2) { + // Limit to width we can actually display, limited by rowbitmap_t + tmp.device_width = std::min(tmp.device_width, kMaxFontWidth); + // parsed. + } + else if (sscanf(buffer, "BBX %d %d %d %d", &tmp.width, &tmp.height, + &tmp.x_offset, &tmp.y_offset) == 4) { + current_glyph = new Glyph(); + *current_glyph = tmp; + current_glyph->bitmap.resize(tmp.height); + row = -1; // let's not start yet, wait for BITMAP + } + else if (strncmp(buffer, "BITMAP", strlen("BITMAP")) == 0) { + row = 0; + } + else if (current_glyph && row >= 0 && row < current_glyph->height + && parseBitmap(buffer, ¤t_glyph->bitmap[row])) { + current_glyph->bitmap[row] >>= current_glyph->x_offset; + row++; + } + else if (strncmp(buffer, "ENDCHAR", strlen("ENDCHAR")) == 0) { + if (current_glyph && row == current_glyph->height) { + delete glyphs_[codepoint]; // just in case there was one. + glyphs_[codepoint] = current_glyph; + current_glyph = NULL; + } + } + +} + +Font *Font::CreateOutlineFont() const { + Font *r = new Font(); + const int kBorder = 1; + r->font_height_ = font_height_ + 2*kBorder; + r->base_line_ = base_line_ + kBorder; + for (CodepointGlyphMap::const_iterator it = glyphs_.begin(); + it != glyphs_.end(); ++it) { + const Glyph *orig = it->second; + const int height = orig->height + 2 * kBorder; + Glyph *const tmp_glyph = new Glyph(); + tmp_glyph->bitmap.resize(height); + tmp_glyph->width = orig->width + 2*kBorder; + tmp_glyph->height = height; + tmp_glyph->device_width = orig->device_width + 2*kBorder; + tmp_glyph->device_height = height; + tmp_glyph->y_offset = orig->y_offset - kBorder; + // TODO: we don't really need bounding box, right ? + const rowbitmap_t fill_pattern = 0b111; + const rowbitmap_t start_mask = 0b010; + // Fill the border + for (int h = 0; h < orig->height; ++h) { + rowbitmap_t fill = fill_pattern; + rowbitmap_t orig_bitmap = orig->bitmap[h] >> kBorder; + for (rowbitmap_t m = start_mask; m.any(); m <<= 1, fill <<= 1) { + if ((orig_bitmap & m).any()) { + tmp_glyph->bitmap[h+kBorder-1] |= fill; + tmp_glyph->bitmap[h+kBorder+0] |= fill; + tmp_glyph->bitmap[h+kBorder+1] |= fill; + } + } + } + // Remove original font again. + for (int h = 0; h < orig->height; ++h) { + rowbitmap_t orig_bitmap = orig->bitmap[h] >> kBorder; + tmp_glyph->bitmap[h+kBorder] &= ~orig_bitmap; + } + r->glyphs_[it->first] = tmp_glyph; + } + return r; +} + +const Font::Glyph *Font::FindGlyph(uint32_t unicode_codepoint) const { + CodepointGlyphMap::const_iterator found = glyphs_.find(unicode_codepoint); + if (found == glyphs_.end()) + return NULL; + return found->second; +} + +int Font::CharacterWidth(uint32_t unicode_codepoint) const { + const Glyph *g = FindGlyph(unicode_codepoint); + return g ? g->device_width : -1; +} + +int Font::DrawGlyph(Canvas *c, int x_pos, int y_pos, + const Color &color, const Color *bgcolor, + uint32_t unicode_codepoint) const { + const Glyph *g = FindGlyph(unicode_codepoint); + if (g == NULL) g = FindGlyph(kUnicodeReplacementCodepoint); + if (g == NULL) return 0; + y_pos = y_pos - g->height - g->y_offset; + + if (x_pos + g->device_width < 0 || x_pos > c->width() || + y_pos + g->height < 0 || y_pos > c->height()) { + return g->device_width; // Outside canvas border. Bail out early. + } + + for (int y = 0; y < g->height; ++y) { + const rowbitmap_t& row = g->bitmap[y]; + for (int x = 0; x < g->device_width; ++x) { + if (row.test(kMaxFontWidth - 1 - x)) { + c->SetPixel(x_pos + x, y_pos + y, color.r, color.g, color.b); + } else if (bgcolor) { + c->SetPixel(x_pos + x, y_pos + y, bgcolor->r, bgcolor->g, bgcolor->b); + } + } + } + return g->device_width; +} + +int Font::DrawGlyph(Canvas *c, int x_pos, int y_pos, const Color &color, + uint32_t unicode_codepoint) const { + return DrawGlyph(c, x_pos, y_pos, color, NULL, unicode_codepoint); +} + +} // namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/content-streamer-c.cc b/deps/rpi-rgb-led-matrix/lib/content-streamer-c.cc new file mode 100644 index 000000000000..7b2cfabae34b --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/content-streamer-c.cc @@ -0,0 +1,42 @@ +// C wrapper implementations for content-streamer C API +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- + +#include "content-streamer.h" +#include "content-streamer-c.h" +#include +#include + +extern "C" { + +StreamIO* file_stream_io_create(const char* filename) { + return reinterpret_cast(new rgb_matrix::FileStreamIO(open(filename, O_RDONLY))); +} + +void file_stream_io_delete(StreamIO* io) { + delete reinterpret_cast(io); +} + +ContentStreamReaderHandle content_stream_reader_create(StreamIO* io) { + return reinterpret_cast(new rgb_matrix::StreamReader(reinterpret_cast(io))); +} + +void content_stream_reader_destroy(ContentStreamReaderHandle reader) { + delete reinterpret_cast(reader); +} + +int content_stream_reader_get_next(ContentStreamReaderHandle reader, struct FrameCanvas* frame, uint32_t* hold_time_us) { + auto r = reinterpret_cast(reader); + return r->GetNext(reinterpret_cast(frame), hold_time_us) ? 1 : 0; +} + +void content_stream_reader_rewind(ContentStreamReaderHandle reader) { + auto r = reinterpret_cast(reader); + r->Rewind(); +} + +// C API wrapper for canvas-aware compatibility check. +int file_stream_io_is_compatible_with_canvas(StreamIO* io, struct FrameCanvas* frame) { + return rgb_matrix::StreamIOIsCompatibleWithCanvas(reinterpret_cast(io), reinterpret_cast(frame)) ? 1 : 0; +} + +} // extern "C" diff --git a/deps/rpi-rgb-led-matrix/lib/content-streamer.cc b/deps/rpi-rgb-led-matrix/lib/content-streamer.cc new file mode 100644 index 000000000000..ecf5da2b821d --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/content-streamer.cc @@ -0,0 +1,256 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- + +#include "content-streamer.h" +#include "led-matrix.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gpio-bits.h" + +namespace rgb_matrix { + +// Pre-c++11 helper +#define STATIC_ASSERT(msg, c) typedef int static_assert_##msg[(c) ? 1 : -1] + +namespace { +// We write magic values as integers to automatically detect endian issues. +// Streams are stored in little-endian. This is the ARM default (running +// the Raspberry Pi, but also x86; so it is possible to create streams easily +// on a different x86 Linux PC. +static const uint32_t kFileMagicValue = 0xED0C5A48; +struct FileHeader { + uint32_t magic; // kFileMagicValue + uint32_t buf_size; + uint32_t width; + uint32_t height; + uint64_t future_use1; + uint64_t is_wide_gpio : 1; + uint64_t flags_future_use : 63; +}; +STATIC_ASSERT(file_header_size_changed, sizeof(FileHeader) == 32); + +static const uint32_t kFrameMagicValue = 0x12345678; +struct FrameHeader { + uint32_t magic; // kFrameMagic + uint32_t size; + uint32_t hold_time_us; // How long this frame lasts in usec. + uint32_t future_use1; + uint64_t future_use2; + uint64_t future_use3; +}; +STATIC_ASSERT(file_header_size_changed, sizeof(FrameHeader) == 32); +} + +FileStreamIO::FileStreamIO(int fd) : fd_(fd) { + posix_fadvise(fd_, 0, 0, POSIX_FADV_SEQUENTIAL); +} +FileStreamIO::~FileStreamIO() { close(fd_); } + +void FileStreamIO::Rewind() { lseek(fd_, 0, SEEK_SET); } + +ssize_t FileStreamIO::Read(void *buf, const size_t count) { + return read(fd_, buf, count); +} + +ssize_t FileStreamIO::Append(const void *buf, const size_t count) { + return write(fd_, buf, count); +} + +void MemStreamIO::Rewind() { pos_ = 0; } +ssize_t MemStreamIO::Read(void *buf, size_t count) { + const size_t amount = std::min(count, buffer_.size() - pos_); + memcpy(buf, buffer_.data() + pos_, amount); + pos_ += amount; + return amount; +} +ssize_t MemStreamIO::Append(const void *buf, size_t count) { + buffer_.append((const char*)buf, count); + return count; +} + +MemMapViewInput::MemMapViewInput(int fd) : buffer_(nullptr) { + struct stat s; + if (fstat(fd, &s) < 0) { + close(fd); + perror("Couldn't get size"); + return; // Can't return error state from constructor. Stay uninitialized. + } + + const size_t file_size = s.st_size; + buffer_ = (char*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + if (buffer_ == MAP_FAILED) { + perror("Can't mmmap()"); + return; + } + end_ = buffer_ + file_size; +#ifdef POSIX_MADV_WILLNEED + // Trigger read-ahead if possible. + posix_madvise(buffer_, file_size, POSIX_MADV_WILLNEED); +#endif +} + +void MemMapViewInput::Rewind() { pos_ = buffer_; } +ssize_t MemMapViewInput::Read(void *buf, size_t count) { + if (pos_ + count >= end_) return -1; + memcpy(buf, pos_, count); + pos_ += count; + return count; +} + +MemMapViewInput::~MemMapViewInput() { + if (buffer_) munmap(buffer_, end_ - buffer_); +} + +// Read exactly count bytes including retries. Returns success. +static bool FullRead(StreamIO *io, void *buf, const size_t count) { + int remaining = count; + char *char_buffer = (char*)buf; + while (remaining > 0) { + int r = io->Read(char_buffer, remaining); + if (r < 0) return false; + if (r == 0) break; // EOF. + char_buffer += r; remaining -= r; + } + return remaining == 0; +} + +// Write exactly count bytes including retries. Returns success. +static bool FullAppend(StreamIO *io, const void *buf, const size_t count) { + int remaining = count; + const char *char_buffer = (const char*) buf; + while (remaining > 0) { + int w = io->Append(char_buffer, remaining); + if (w < 0) return false; + char_buffer += w; remaining -= w; + } + return remaining == 0; +} + +StreamWriter::StreamWriter(StreamIO *io) : io_(io), header_written_(false) {} +bool StreamWriter::Stream(const FrameCanvas &frame, uint32_t hold_time_us) { + const char *data; + size_t len; + frame.Serialize(&data, &len); + + if (!header_written_) { + WriteFileHeader(frame, len); + } + FrameHeader h = {}; + h.magic = kFrameMagicValue; + h.size = len; + h.hold_time_us = hold_time_us; + FullAppend(io_, &h, sizeof(h)); + return FullAppend(io_, data, len) == (ssize_t)len; +} + +void StreamWriter::WriteFileHeader(const FrameCanvas &frame, size_t len) { + FileHeader header = {}; + header.magic = kFileMagicValue; + header.width = frame.width(); + header.height = frame.height(); + header.buf_size = len; + header.is_wide_gpio = (sizeof(gpio_bits_t) > 4); + FullAppend(io_, &header, sizeof(header)); + header_written_ = true; +} + +StreamReader::StreamReader(StreamIO *io) + : io_(io), state_(STREAM_AT_BEGIN), header_frame_buffer_(NULL) { + io_->Rewind(); +} +StreamReader::~StreamReader() { delete [] header_frame_buffer_; } + +void StreamReader::Rewind() { + io_->Rewind(); + state_ = STREAM_AT_BEGIN; +} + +bool StreamReader::GetNext(FrameCanvas *frame, uint32_t* hold_time_us) { + if (state_ == STREAM_AT_BEGIN && !ReadFileHeader(*frame)) return false; + if (state_ != STREAM_READING) return false; + + // Read header and expected buffer size. + if (!FullRead(io_, header_frame_buffer_, + sizeof(FrameHeader) + frame_buf_size_)) { + return false; + } + + const FrameHeader &h = *reinterpret_cast(header_frame_buffer_); + + // TODO: we might allow for this to be a kFileMagicValue, to allow people + // to just concatenate streams. In that case, we just would need to read + // ahead past this header (both headers are designed to be same size) + if (h.magic != kFrameMagicValue) { + state_ = STREAM_ERROR; + return false; + } + + // In the future, we might allow larger buffers (audio?), but never smaller. + // For now, we need to make sure to exactly match the size, as our assumption + // above is that we can read the full header + frame in one FullRead(). + if (h.size != frame_buf_size_) + return false; + + if (hold_time_us) *hold_time_us = h.hold_time_us; + return frame->Deserialize(header_frame_buffer_ + sizeof(FrameHeader), + frame_buf_size_); +} + +bool StreamReader::ReadFileHeader(const FrameCanvas &frame) { + FileHeader header; + FullRead(io_, &header, sizeof(header)); + if (header.magic != kFileMagicValue) { + state_ = STREAM_ERROR; + return false; + } + if ((int)header.width != frame.width() + || (int)header.height != frame.height()) { + fprintf(stderr, "This stream is for %dx%d, can't play on %dx%d. " + "Please use the same settings for record/replay\n", + header.width, header.height, frame.width(), frame.height()); + state_ = STREAM_ERROR; + return false; + } + if (header.is_wide_gpio != (sizeof(gpio_bits_t) == 8)) { + fprintf(stderr, "This stream was written with %s GPIO width support but " + "this library is compiled with %d bit GPIO width (see " + "ENABLE_WIDE_GPIO_COMPUTE_MODULE setting in lib/Makefile)\n", + header.is_wide_gpio ? "wide (64-bit)" : "narrow (32-bit)", + int(sizeof(gpio_bits_t) * 8)); + state_ = STREAM_ERROR; + return false; + } + state_ = STREAM_READING; + frame_buf_size_ = header.buf_size; + if (!header_frame_buffer_) + header_frame_buffer_ = new char [ sizeof(FrameHeader) + header.buf_size ]; + return true; +} +// Namespace-scoped helper for canvas-aware compatibility so it can access +// anonymous constants like kFileMagicValue and FullRead. +bool StreamIOIsCompatibleWithCanvas(StreamIO* io, FrameCanvas* frame) { + if (!io || !frame) return false; + io->Rewind(); + FileHeader header; + if (!FullRead(io, &header, sizeof(header))) { io->Rewind(); return false; } + if (header.magic != kFileMagicValue) { io->Rewind(); return false; } + if ((int)header.width != frame->width() || (int)header.height != frame->height()) { io->Rewind(); return false; } + if (header.is_wide_gpio != (sizeof(gpio_bits_t) == 8)) { io->Rewind(); return false; } + const char* data = nullptr; + size_t len = 0; + frame->Serialize(&data, &len); + if (header.buf_size != len) { io->Rewind(); return false; } + io->Rewind(); + return true; +} +} // namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/framebuffer-internal.h b/deps/rpi-rgb-led-matrix/lib/framebuffer-internal.h new file mode 100644 index 000000000000..01e52c77cbc4 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/framebuffer-internal.h @@ -0,0 +1,192 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see +#ifndef RPI_RGBMATRIX_FRAMEBUFFER_INTERNAL_H +#define RPI_RGBMATRIX_FRAMEBUFFER_INTERNAL_H + +#include +#include + +#include + +#include "hardware-mapping.h" +#include "../include/graphics.h" + +namespace rgb_matrix { +class GPIO; +class PinPulser; +namespace internal { +class RowAddressSetter; + +// An opaque type used within the framebuffer that can be used +// to copy between PixelMappers. +struct PixelDesignator { + PixelDesignator() : gpio_word(-1), r_bit(0), g_bit(0), b_bit(0), mask(~0u){} + long gpio_word; + gpio_bits_t r_bit; + gpio_bits_t g_bit; + gpio_bits_t b_bit; + gpio_bits_t mask; +}; + +class PixelDesignatorMap { +public: + PixelDesignatorMap(int width, int height, const PixelDesignator &fill_bits); + + // Get a writable version of the PixelDesignator. Outside Framebuffer used + // by the RGBMatrix to re-assign mappings to new PixelDesignatorMappers. + PixelDesignator *get(int x, int y); + + inline int width() const { return width_; } + inline int height() const { return height_; } + + // All bits that set red/green/blue pixels; used for Fill(). + const PixelDesignator &GetFillColorBits() { return fill_bits_; } + +private: + const int width_; + const int height_; + const PixelDesignator fill_bits_; // Precalculated for fill. + std::vector buffer_; +}; + +// Internal representation of the frame-buffer that as well can +// write itself to GPIO. +// Our internal memory layout mimicks as much as possible what needs to be +// written out. +class Framebuffer { +public: + // Maximum usable bitplanes. + // + // 11 bits seems to be a sweet spot in which we still get somewhat useful + // refresh rate and have good color richness. This is the default setting + // However, in low-light situations, we want to be able to scale down + // brightness more, having more bits at the bottom. + // TODO(hzeller): make the default 15 bit or so, but slide the use of + // timing to lower bits if fewer bits requested to not affect the overall + // refresh in that case. + // This needs to be balanced to not create too aggressive timing however. + // To be explored in a separate commit. + // + // For now, if someone needs very low level of light, change this to + // say 13 and recompile. Run with --led-pwm-bits=13. Also, consider + // --led-pwm-dither-bits=2 to have the refresh rate not suffer too much. + static constexpr int kBitPlanes = 11; + static constexpr int kDefaultBitPlanes = 11; + + Framebuffer(int rows, int columns, int parallel, + int scan_mode, + const char* led_sequence, bool inverse_color, + PixelDesignatorMap **mapper); + ~Framebuffer(); + + // Initialize GPIO bits for output. Only call once. + static void InitHardwareMapping(const char *named_hardware); + static void InitGPIO(GPIO *io, int rows, int parallel, + bool allow_hardware_pulsing, + int pwm_lsb_nanoseconds, + int dither_bits, + int row_address_type); + static void InitializePanels(GPIO *io, const char *panel_type, int columns); + // Reset internal static globals so InitGPIO() can re-run with new params. + static void ResetGlobals(); + + // Set PWM bits used for output. Default is 11, but if you only deal with + // simple comic-colors, 1 might be sufficient. Lower require less CPU. + // Returns boolean to signify if value was within range. + bool SetPWMBits(uint8_t value); + uint8_t pwmbits() { return pwm_bits_; } + + // Map brightness of output linearly to input with CIE1931 profile. + void set_luminance_correct(bool on) { do_luminance_correct_ = on; } + bool luminance_correct() const { return do_luminance_correct_; } + + // Set brightness in percent; range=1..100 + // This will only affect newly set pixels. + void SetBrightness(uint8_t b) { + brightness_ = (b <= 100 ? (b != 0 ? b : 1) : 100); + } + uint8_t brightness() { return brightness_; } + + void DumpToMatrix(GPIO *io, int pwm_bits_to_show); + + void Serialize(const char **data, size_t *len) const; + bool Deserialize(const char *data, size_t len); + void CopyFrom(const Framebuffer *other); + + // Canvas-inspired methods, but we're not implementing this interface to not + // have an unnecessary vtable. + int width() const; + int height() const; + void SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue); + void SetPixels(int x, int y, int width, int height, Color *colors); + void Clear(); + void Fill(uint8_t red, uint8_t green, uint8_t blue); + void SubFill(int x, int y, int width, int height, uint8_t red, uint8_t green, uint8_t blue); + + const struct HardwareMapping &hardware_mapping() const { + return *hardware_mapping_; + } + int columns() const { return columns_; } + int scan_mode() const { return scan_mode_; } + int double_rows() const { return double_rows_; } + const gpio_bits_t *RowDataAt(int double_row, int bit) const { + return &bitplane_buffer_[double_row * (columns_ * kBitPlanes) + + bit * columns_]; + } + +private: + static const struct HardwareMapping *hardware_mapping_; + static RowAddressSetter *row_setter_; + + // This returns the gpio-bit for given color (one of 'R', 'G', 'B'). This is + // returning the right value in case "led_sequence" is _not_ "RGB" + static gpio_bits_t GetGpioFromLedSequence(char col, const char *led_sequence, + gpio_bits_t default_r, + gpio_bits_t default_g, + gpio_bits_t default_b); + + void InitDefaultDesignator(int x, int y, const char *led_sequence, + PixelDesignator *designator); + inline void MapColors(uint8_t r, uint8_t g, uint8_t b, + uint16_t *red, uint16_t *green, uint16_t *blue); + const int rows_; // Number of rows. 16 or 32. + const int parallel_; // Parallel rows of chains. 1 or 2. + const int height_; // rows * parallel + const int columns_; // Number of columns. Number of chained boards * 32. + + const int scan_mode_; + const bool inverse_color_; + + uint8_t pwm_bits_; // PWM bits to display. + bool do_luminance_correct_; + uint8_t brightness_; + + const int double_rows_; + const size_t buffer_size_; + + // The frame-buffer is organized in bitplanes. + // Highest level (slowest to cycle through) are double rows. + // For each double-row, we store pwm-bits columns of a bitplane. + // Each bitplane-column is pre-filled IoBits, of which the colors are set. + // Of course, that means that we store unrelated bits in the frame-buffer, + // but it allows easy access in the critical section. + gpio_bits_t *bitplane_buffer_; + inline gpio_bits_t *ValueAt(int double_row, int column, int bit); + + PixelDesignatorMap **shared_mapper_; // Storage in RGBMatrix. +}; +} // namespace internal +} // namespace rgb_matrix +#endif // RPI_RGBMATRIX_FRAMEBUFFER_INTERNAL_H diff --git a/deps/rpi-rgb-led-matrix/lib/framebuffer.cc b/deps/rpi-rgb-led-matrix/lib/framebuffer.cc new file mode 100644 index 000000000000..36d138b6c733 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/framebuffer.cc @@ -0,0 +1,1034 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see + +// The framebuffer is the workhorse: it represents the frame in some internal +// format that is friendly to be dumped to the matrix quickly. Provides methods +// to manipulate the content. + +#include "framebuffer-internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gpio.h" +#include "rp1/rp1_pio_backend.h" +#include "rp1/rp1_rio_backend.h" +#include "../include/graphics.h" + +namespace rgb_matrix { +namespace internal { +// We need one global instance of a timing correct pulser. There are different +// implementations depending on the context. +static PinPulser *sOutputEnablePulser = NULL; + +#ifdef ONLY_SINGLE_SUB_PANEL +# define SUB_PANELS_ 1 +#else +# define SUB_PANELS_ 2 +#endif + +PixelDesignator *PixelDesignatorMap::get(int x, int y) { + if (x < 0 || y < 0 || x >= width_ || y >= height_) + return NULL; + return &buffer_[(y*width_) + x]; +} + +PixelDesignatorMap::PixelDesignatorMap(int width, int height, + const PixelDesignator &fill_bits) + : width_(width), + height_(height), + fill_bits_(fill_bits), + buffer_(width * height) { +} + +// Different panel types use different techniques to set the row address. +// We abstract that away with different implementations of RowAddressSetter +class RowAddressSetter { +public: + virtual ~RowAddressSetter() {} + virtual gpio_bits_t need_bits() const = 0; + virtual void SetRowAddress(GPIO *io, int row) = 0; +}; + +namespace { + +// The default DirectRowAddressSetter just sets the address in parallel +// output lines ABCDE with A the LSB and E the MSB. +class DirectRowAddressSetter : public RowAddressSetter { +public: + DirectRowAddressSetter(int double_rows, const HardwareMapping &h) + : row_mask_(0), last_row_(-1) { + assert(double_rows <= 32); // need to resize row_lookup_ + if (double_rows > 16) row_mask_ |= h.e; + if (double_rows > 8) row_mask_ |= h.d; + if (double_rows > 4) row_mask_ |= h.c; + if (double_rows > 2) row_mask_ |= h.b; + row_mask_ |= h.a; + for (int i = 0; i < double_rows; ++i) { + // To avoid the bit-fiddle in the critical path, utilize + // a lookup-table for all possible rows. + gpio_bits_t row_address = (i & 0x01) ? h.a : 0; + row_address |= (i & 0x02) ? h.b : 0; + row_address |= (i & 0x04) ? h.c : 0; + row_address |= (i & 0x08) ? h.d : 0; + row_address |= (i & 0x10) ? h.e : 0; + row_lookup_[i] = row_address; + } + } + + virtual gpio_bits_t need_bits() const { return row_mask_; } + + virtual void SetRowAddress(GPIO *io, int row) { + if (row == last_row_) return; + io->WriteMaskedBits(row_lookup_[row], row_mask_); + last_row_ = row; + } + +private: + gpio_bits_t row_mask_; + gpio_bits_t row_lookup_[32]; + int last_row_; +}; + +// The SM5266RowAddressSetter (ABC Shifter + DE direct) sets bits ABC using +// a 8 bit shifter and DE directly. The panel this works with has 8 SM5266 +// shifters (4 for the top 32 rows and 4 for the bottom 32 rows). +// DE is used to select the active shifter +// (rows 1-8/33-40, 9-16/41-48, 17-24/49-56, 25-32/57-64). +// Rows are enabled by shifting in 8 bits (high bit first) with a high bit +// enabling that row. This allows up to 8 rows per group to be active at the +// same time (if they have the same content), but that isn't implemented here. +// BK, DIN and DCK are the designations on the SM5266P datasheet. +// BK = Enable Input, DIN = Serial In, DCK = Clock +class SM5266RowAddressSetter : public RowAddressSetter { +public: + SM5266RowAddressSetter(int double_rows, const HardwareMapping &h) + : row_mask_(h.a | h.b | h.c), + last_row_(-1), + bk_(h.c), + din_(h.b), + dck_(h.a) { + assert(double_rows <= 32); // designed for up to 1/32 panel + if (double_rows > 8) row_mask_ |= h.d; + if (double_rows > 16) row_mask_ |= h.e; + for (int i = 0; i < double_rows; ++i) { + gpio_bits_t row_address = 0; + row_address |= (i & 0x08) ? h.d : 0; + row_address |= (i & 0x10) ? h.e : 0; + row_lookup_[i] = row_address; + } + } + + virtual gpio_bits_t need_bits() const { return row_mask_; } + + virtual void SetRowAddress(GPIO *io, int row) { + if (row == last_row_) return; + io->SetBits(bk_); // Enable serial input for the shifter + for (int r = 7; r >= 0; r--) { + if (row % 8 == r) { + io->SetBits(din_); + } else { + io->ClearBits(din_); + } + io->SetBits(dck_); + io->SetBits(dck_); // Longer clock time; tested with Pi3 + io->ClearBits(dck_); + } + io->ClearBits(bk_); // Disable serial input to keep unwanted bits out of the shifters + last_row_ = row; + // Set bits D and E to enable the proper shifter to display the selected + // row. + io->WriteMaskedBits(row_lookup_[row], row_mask_); + } + +private: + gpio_bits_t row_mask_; + int last_row_; + const gpio_bits_t bk_; + const gpio_bits_t din_; + const gpio_bits_t dck_; + gpio_bits_t row_lookup_[32]; +}; + +class B707ShiftRegisterRowAddressSetter : public RowAddressSetter { +public: + B707ShiftRegisterRowAddressSetter(int double_rows, const HardwareMapping &h) + : row_mask_(h.a | h.b | h.c), + last_row_(-1), + bk_(h.b), + din_(h.c), + dck_(h.a) { + assert(double_rows <= 32); // designed for up to 1/32 panel + } + + virtual gpio_bits_t need_bits() const { return row_mask_; } + + virtual void SetRowAddress(GPIO *io, int row) { + if (row == last_row_) return; + io->SetBits(bk_); // Enable serial input for the shifter + if (row == 0) { + io->SetBits(din_); + } else { + io->ClearBits(din_); + } + io->SetBits(dck_); + io->SetBits(dck_); // Longer clock time; tested with Pi3 + io->ClearBits(dck_); + io->ClearBits(bk_); // Disable serial input to keep unwanted bits out of the shifters + last_row_ = row; + } + +private: + gpio_bits_t row_mask_; + int last_row_; + const gpio_bits_t bk_; + const gpio_bits_t din_; + const gpio_bits_t dck_; +}; + + +class ShiftRegisterRowAddressSetter : public RowAddressSetter { +public: + ShiftRegisterRowAddressSetter(int double_rows, const HardwareMapping &h) + : double_rows_(double_rows), + row_mask_(h.a | h.b), clock_(h.a), data_(h.b), + last_row_(-1) { + } + virtual gpio_bits_t need_bits() const { return row_mask_; } + + virtual void SetRowAddress(GPIO *io, int row) { + if (row == last_row_) return; + for (int activate = 0; activate < double_rows_; ++activate) { + io->ClearBits(clock_); + if (activate == double_rows_ - 1 - row) { + io->ClearBits(data_); + } else { + io->SetBits(data_); + } + io->SetBits(clock_); + } + io->ClearBits(clock_); + io->SetBits(clock_); + last_row_ = row; + } + +private: + const int double_rows_; + const gpio_bits_t row_mask_; + const gpio_bits_t clock_; + const gpio_bits_t data_; + int last_row_; +}; + +// Issue #823 +// An shift register row address setter that does not use B but C for the +// data. Clock is inverted. +class ABCShiftRegisterRowAddressSetter : public RowAddressSetter { +public: + ABCShiftRegisterRowAddressSetter(int double_rows, const HardwareMapping &h) + : double_rows_(double_rows), + row_mask_(h.a | h.c), + clock_(h.a), + data_(h.c), + last_row_(-1) { + } + virtual gpio_bits_t need_bits() const { return row_mask_; } + + virtual void SetRowAddress(GPIO *io, int row) { + for (int activate = 0; activate < double_rows_; ++activate) { + io->ClearBits(clock_); + if (activate == double_rows_ - 1 - row) { + io->SetBits(data_); + } else { + io->ClearBits(data_); + } + io->SetBits(clock_); + } + io->SetBits(clock_); + io->ClearBits(clock_); + last_row_ = row; + } + +private: + const int double_rows_; + const gpio_bits_t row_mask_; + const gpio_bits_t clock_; + const gpio_bits_t data_; + int last_row_; +}; + +// The DirectABCDRowAddressSetter sets the address by one of +// row pin ABCD for 32х16 matrix 1:4 multiplexing. The matrix has +// 4 addressable rows. Row is selected by a low level on the +// corresponding row address pin. Other row address pins must be in high level. +// +// Row addr| 0 | 1 | 2 | 3 +// --------+---+---+---+--- +// Line A | 0 | 1 | 1 | 1 +// Line B | 1 | 0 | 1 | 1 +// Line C | 1 | 1 | 0 | 1 +// Line D | 1 | 1 | 1 | 0 +class DirectABCDLineRowAddressSetter : public RowAddressSetter { +public: + DirectABCDLineRowAddressSetter(int double_rows, const HardwareMapping &h) + : last_row_(-1) { + row_mask_ = h.a | h.b | h.c | h.d; + + row_lines_[0] = /*h.a |*/ h.b | h.c | h.d; + row_lines_[1] = h.a /*| h.b*/ | h.c | h.d; + row_lines_[2] = h.a | h.b /*| h.c */| h.d; + row_lines_[3] = h.a | h.b | h.c /*| h.d*/; + } + + virtual gpio_bits_t need_bits() const { return row_mask_; } + + virtual void SetRowAddress(GPIO *io, int row) { + if (row == last_row_) return; + + gpio_bits_t row_address = row_lines_[row % 4]; + + io->WriteMaskedBits(row_address, row_mask_); + last_row_ = row; + } + +private: + gpio_bits_t row_lines_[4]; + gpio_bits_t row_mask_; + int last_row_; +}; + +} + +const struct HardwareMapping *Framebuffer::hardware_mapping_ = NULL; +RowAddressSetter *Framebuffer::row_setter_ = NULL; + +Framebuffer::Framebuffer(int rows, int columns, int parallel, + int scan_mode, + const char *led_sequence, bool inverse_color, + PixelDesignatorMap **mapper) + : rows_(rows), + parallel_(parallel), + height_(rows * parallel), + columns_(columns), + scan_mode_(scan_mode), + inverse_color_(inverse_color), + pwm_bits_(kBitPlanes), do_luminance_correct_(true), brightness_(100), + double_rows_(rows / SUB_PANELS_), + buffer_size_(double_rows_ * columns_ * kBitPlanes * sizeof(gpio_bits_t)), + shared_mapper_(mapper) { + assert(hardware_mapping_ != NULL); // Called InitHardwareMapping() ? + assert(shared_mapper_ != NULL); // Storage should be provided by RGBMatrix. + assert(rows_ >=4 && rows_ <= 64 && rows_ % 2 == 0); + if (parallel > hardware_mapping_->max_parallel_chains) { + fprintf(stderr, "The %s GPIO mapping only supports %d parallel chain%s, " + "but %d was requested.\n", hardware_mapping_->name, + hardware_mapping_->max_parallel_chains, + hardware_mapping_->max_parallel_chains > 1 ? "s" : "", parallel); + abort(); + } + assert(parallel >= 1 && parallel <= 6); + + bitplane_buffer_ = new gpio_bits_t[double_rows_ * columns_ * kBitPlanes]; + + // If we're the first Framebuffer created, the shared PixelMapper is + // still NULL, so create one. + // The first PixelMapper represents the physical layout of a standard matrix + // with the specific knowledge of the framebuffer, setting up PixelDesignators + // in a way that they are useful for this Framebuffer. + // + // Newly created PixelMappers then can just re-arrange PixelDesignators + // from the parent PixelMapper opaquely without having to know the details. + if (*shared_mapper_ == NULL) { + // Gather all the bits for given color for fast Fill()s and use the right + // bits according to the led sequence + const struct HardwareMapping &h = *hardware_mapping_; + gpio_bits_t r = h.p0_r1 | h.p0_r2 | h.p1_r1 | h.p1_r2 | h.p2_r1 | h.p2_r2 | h.p3_r1 | h.p3_r2 | h.p4_r1 | h.p4_r2 | h.p5_r1 | h.p5_r2; + gpio_bits_t g = h.p0_g1 | h.p0_g2 | h.p1_g1 | h.p1_g2 | h.p2_g1 | h.p2_g2 | h.p3_g1 | h.p3_g2 | h.p4_g1 | h.p4_g2 | h.p5_g1 | h.p5_g2; + gpio_bits_t b = h.p0_b1 | h.p0_b2 | h.p1_b1 | h.p1_b2 | h.p2_b1 | h.p2_b2 | h.p3_b1 | h.p3_b2 | h.p4_b1 | h.p4_b2 | h.p5_b1 | h.p5_b2; + PixelDesignator fill_bits; + fill_bits.r_bit = GetGpioFromLedSequence('R', led_sequence, r, g, b); + fill_bits.g_bit = GetGpioFromLedSequence('G', led_sequence, r, g, b); + fill_bits.b_bit = GetGpioFromLedSequence('B', led_sequence, r, g, b); + + *shared_mapper_ = new PixelDesignatorMap(columns_, height_, fill_bits); + for (int y = 0; y < height_; ++y) { + for (int x = 0; x < columns_; ++x) { + InitDefaultDesignator(x, y, led_sequence, (*shared_mapper_)->get(x, y)); + } + } + } + + Clear(); +} + +Framebuffer::~Framebuffer() { + delete [] bitplane_buffer_; +} + +// TODO: this should also be parsed from some special formatted string, e.g. +// {addr={22,23,24,25,15},oe=18,clk=17,strobe=4, p0={11,27,7,8,9,10},...} +/* static */ void Framebuffer::InitHardwareMapping(const char *named_hardware) { + if (named_hardware == NULL || *named_hardware == '\0') { + named_hardware = "regular"; + } + + struct HardwareMapping *mapping = NULL; + for (HardwareMapping *it = matrix_hardware_mappings; it->name; ++it) { + if (strcasecmp(it->name, named_hardware) == 0) { + mapping = it; + break; + } + } + + if (!mapping) { + fprintf(stderr, "There is no hardware mapping named '%s'.\nAvailable: ", + named_hardware); + for (HardwareMapping *it = matrix_hardware_mappings; it->name; ++it) { + if (it != matrix_hardware_mappings) fprintf(stderr, ", "); + fprintf(stderr, "'%s'", it->name); + } + fprintf(stderr, "\n"); + abort(); + } + + if (mapping->max_parallel_chains == 0) { + // Auto determine. + struct HardwareMapping *h = mapping; + if ((h->p0_r1 | h->p0_g1 | h->p0_g1 | h->p0_r2 | h->p0_g2 | h->p0_g2) > 0) + ++mapping->max_parallel_chains; + if ((h->p1_r1 | h->p1_g1 | h->p1_g1 | h->p1_r2 | h->p1_g2 | h->p1_g2) > 0) + ++mapping->max_parallel_chains; + if ((h->p2_r1 | h->p2_g1 | h->p2_g1 | h->p2_r2 | h->p2_g2 | h->p2_g2) > 0) + ++mapping->max_parallel_chains; + if ((h->p3_r1 | h->p3_g1 | h->p3_g1 | h->p3_r2 | h->p3_g2 | h->p3_g2) > 0) + ++mapping->max_parallel_chains; + if ((h->p4_r1 | h->p4_g1 | h->p4_g1 | h->p4_r2 | h->p4_g2 | h->p4_g2) > 0) + ++mapping->max_parallel_chains; + if ((h->p5_r1 | h->p5_g1 | h->p5_g1 | h->p5_r2 | h->p5_g2 | h->p5_g2) > 0) + ++mapping->max_parallel_chains; + } + hardware_mapping_ = mapping; +} + +/* static */ void Framebuffer::InitGPIO(GPIO *io, int rows, int parallel, + bool allow_hardware_pulsing, + int pwm_lsb_nanoseconds, + int dither_bits, + int row_address_type) { + if (sOutputEnablePulser != NULL) + return; // already initialized. + + const struct HardwareMapping &h = *hardware_mapping_; + const int double_rows = rows / SUB_PANELS_; + + if (Rp1RioShouldActivate(h.name, row_address_type, parallel)) { + Rp1RioInitOrDie(h, double_rows, parallel, pwm_lsb_nanoseconds, dither_bits, + row_address_type); + return; + } + + if (Rp1PioShouldActivate(h.name, row_address_type, parallel)) { + Rp1PioInitOrDie(h, double_rows, parallel, pwm_lsb_nanoseconds, dither_bits, + row_address_type); + return; + } + + // Tell GPIO about all bits we intend to use. + gpio_bits_t all_used_bits = 0; + + all_used_bits |= h.output_enable | h.clock | h.strobe; + + all_used_bits |= h.p0_r1 | h.p0_g1 | h.p0_b1 | h.p0_r2 | h.p0_g2 | h.p0_b2; + if (parallel >= 2) { + all_used_bits |= h.p1_r1 | h.p1_g1 | h.p1_b1 | h.p1_r2 | h.p1_g2 | h.p1_b2; + } + if (parallel >= 3) { + all_used_bits |= h.p2_r1 | h.p2_g1 | h.p2_b1 | h.p2_r2 | h.p2_g2 | h.p2_b2; + } + if (parallel >= 4) { + all_used_bits |= h.p3_r1 | h.p3_g1 | h.p3_b1 | h.p3_r2 | h.p3_g2 | h.p3_b2; + } + if (parallel >= 5) { + all_used_bits |= h.p4_r1 | h.p4_g1 | h.p4_b1 | h.p4_r2 | h.p4_g2 | h.p4_b2; + } + if (parallel >= 6) { + all_used_bits |= h.p5_r1 | h.p5_g1 | h.p5_b1 | h.p5_r2 | h.p5_g2 | h.p5_b2; + } + + switch (row_address_type) { + case 0: + row_setter_ = new DirectRowAddressSetter(double_rows, h); + break; + case 1: + row_setter_ = new ShiftRegisterRowAddressSetter(double_rows, h); + break; + case 2: + row_setter_ = new DirectABCDLineRowAddressSetter(double_rows, h); + break; + case 3: + row_setter_ = new ABCShiftRegisterRowAddressSetter(double_rows, h); + break; + case 4: + row_setter_ = new SM5266RowAddressSetter(double_rows, h); + break; + case 5: + row_setter_ = new B707ShiftRegisterRowAddressSetter(double_rows, h); + break; + + + default: + assert(0); // unexpected type. + } + + all_used_bits |= row_setter_->need_bits(); + + // Adafruit HAT identified by the same prefix. + const bool is_some_adafruit_hat = (0 == strncmp(h.name, "adafruit-hat", + strlen("adafruit-hat"))); + // Initialize outputs, make sure that all of these are supported bits. + const gpio_bits_t result = io->InitOutputs(all_used_bits, + is_some_adafruit_hat); + assert(result == all_used_bits); // Impl: all bits declared in gpio.cc ? + + std::vector bitplane_timings; + uint32_t timing_ns = pwm_lsb_nanoseconds; + for (int b = 0; b < kBitPlanes; ++b) { + bitplane_timings.push_back(timing_ns); + if (b >= dither_bits) timing_ns *= 2; + } + sOutputEnablePulser = PinPulser::Create(io, h.output_enable, + allow_hardware_pulsing, + bitplane_timings); +} + +// NOTE: first version for panel initialization sequence, need to refine +// until it is more clear how different panel types are initialized to be +// able to abstract this more. + +static void InitFM6126(GPIO *io, const struct HardwareMapping &h, int columns) { + const gpio_bits_t bits_on + = h.p0_r1 | h.p0_g1 | h.p0_b1 | h.p0_r2 | h.p0_g2 | h.p0_b2 + | h.p1_r1 | h.p1_g1 | h.p1_b1 | h.p1_r2 | h.p1_g2 | h.p1_b2 + | h.p2_r1 | h.p2_g1 | h.p2_b1 | h.p2_r2 | h.p2_g2 | h.p2_b2 + | h.p3_r1 | h.p3_g1 | h.p3_b1 | h.p3_r2 | h.p3_g2 | h.p3_b2 + | h.p4_r1 | h.p4_g1 | h.p4_b1 | h.p4_r2 | h.p4_g2 | h.p4_b2 + | h.p5_r1 | h.p5_g1 | h.p5_b1 | h.p5_r2 | h.p5_g2 | h.p5_b2 + | h.a; // Address bit 'A' is always on. + const gpio_bits_t bits_off = h.a; + const gpio_bits_t mask = bits_on | h.strobe; + + // Init bits. TODO: customize, as we can do things such as brightness here, + // which would allow more higher quality output. + static const char* init_b12 = "0111111111111111"; // full bright + static const char* init_b13 = "0000000001000000"; // panel on. + + io->ClearBits(h.clock | h.strobe); + + for (int i = 0; i < columns; ++i) { + gpio_bits_t value = init_b12[i % 16] == '0' ? bits_off : bits_on; + if (i > columns - 12) value |= h.strobe; + io->WriteMaskedBits(value, mask); + io->SetBits(h.clock); + io->ClearBits(h.clock); + } + io->ClearBits(h.strobe); + + for (int i = 0; i < columns; ++i) { + gpio_bits_t value = init_b13[i % 16] == '0' ? bits_off : bits_on; + if (i > columns - 13) value |= h.strobe; + io->WriteMaskedBits(value, mask); + io->SetBits(h.clock); + io->ClearBits(h.clock); + } + io->ClearBits(h.strobe); +} + +// The FM6217 is very similar to the FM6216. +// FM6217 adds Register 3 to allow for automatic bad pixel suppression. +static void InitFM6127(GPIO *io, const struct HardwareMapping &h, int columns) { + const gpio_bits_t bits_r_on= h.p0_r1 | h.p0_r2; + const gpio_bits_t bits_g_on= h.p0_g1 | h.p0_g2; + const gpio_bits_t bits_b_on= h.p0_b1 | h.p0_b2; + const gpio_bits_t bits_on= bits_r_on | bits_g_on | bits_b_on; + const gpio_bits_t bits_off = 0; + + const gpio_bits_t mask = bits_on | h.strobe; + + static const char* init_b12 = "1111111111001110"; // register 1 + static const char* init_b13 = "1110000001100010"; // register 2. + static const char* init_b11 = "0101111100000000"; // register 3. + io->ClearBits(h.clock | h.strobe); + for (int i = 0; i < columns; ++i) { + gpio_bits_t value = init_b12[i % 16] == '0' ? bits_off : bits_on; + if (i > columns - 12) value |= h.strobe; + io->WriteMaskedBits(value, mask); + io->SetBits(h.clock); + io->ClearBits(h.clock); + } + io->ClearBits(h.strobe); + + for (int i = 0; i < columns; ++i) { + gpio_bits_t value = init_b13[i % 16] == '0' ? bits_off : bits_on; + if (i > columns - 13) value |= h.strobe; + io->WriteMaskedBits(value, mask); + io->SetBits(h.clock); + io->ClearBits(h.clock); + } + io->ClearBits(h.strobe); + + for (int i = 0; i < columns; ++i) { + gpio_bits_t value = init_b11[i % 16] == '0' ? bits_off : bits_on; + if (i > columns - 11) value |= h.strobe; + io->WriteMaskedBits(value, mask); + io->SetBits(h.clock); + io->ClearBits(h.clock); + } + io->ClearBits(h.strobe); +} + +/*static*/ void Framebuffer::InitializePanels(GPIO *io, + const char *panel_type, + int columns) { + if (!panel_type || panel_type[0] == '\0') return; + if (Rp1RioIsActive()) { + Rp1RioInitializePanels(*hardware_mapping_, panel_type, columns); + return; + } + if (Rp1PioIsActive()) { + Rp1PioInitializePanels(*hardware_mapping_, panel_type, columns); + return; + } + if (strncasecmp(panel_type, "fm6126", 6) == 0) { + InitFM6126(io, *hardware_mapping_, columns); + } + else if (strncasecmp(panel_type, "fm6127", 6) == 0) { + InitFM6127(io, *hardware_mapping_, columns); + } + // else if (strncasecmp(...)) // more init types + else { + fprintf(stderr, "Unknown panel type '%s'; typo ?\n", panel_type); + } +} + +bool Framebuffer::SetPWMBits(uint8_t value) { + if (value < 1 || value > kBitPlanes) + return false; + pwm_bits_ = value; + return true; +} + +inline gpio_bits_t *Framebuffer::ValueAt(int double_row, int column, int bit) { + return &bitplane_buffer_[ double_row * (columns_ * kBitPlanes) + + bit * columns_ + + column ]; +} + +void Framebuffer::Clear() { + if (inverse_color_) { + Fill(0, 0, 0); + } else { + // Cheaper. + memset(bitplane_buffer_, 0, + sizeof(*bitplane_buffer_) * double_rows_ * columns_ * kBitPlanes); + } +} + +struct ColorLookup { + uint16_t color[256]; +}; + +class ColorLookupTable { + public: + static const ColorLookup &GetLookup(uint8_t brightness) { + static ColorLookupTable instance; + return instance.lookups_[brightness - 1]; + } + + + private: + // Do CIE1931 luminance correction and scale to output bitplanes + static uint16_t luminance_cie1931(uint8_t c, uint8_t brightness) { + float out_factor = ((1 << internal::Framebuffer::kBitPlanes) - 1); + float v = (float) c * brightness / 255.0; + return roundf(out_factor * ((v <= 8) ? v / 902.3 : pow((v + 16) / 116.0, 3))); + } + + ColorLookupTable() { + for (int c = 0; c < 256; ++c) + for (int b = 0; b < 100; ++b) + lookups_[b].color[c] = luminance_cie1931(c, b + 1); + } + + ColorLookup lookups_[100]{}; + }; + + static inline uint16_t CIEMapColor(uint8_t brightness, uint8_t c) { + return ColorLookupTable::GetLookup(brightness).color[c]; + } + +// Non luminance correction. TODO: consider getting rid of this. +static inline uint16_t DirectMapColor(uint8_t brightness, uint8_t c) { + // simple scale down the color value + c = c * brightness / 100; + + // shift to be left aligned with top-most bits. + constexpr int shift = internal::Framebuffer::kBitPlanes - 8; + return (shift > 0) ? (c << shift) : (c >> -shift); +} + +inline void Framebuffer::MapColors( + uint8_t r, uint8_t g, uint8_t b, + uint16_t *red, uint16_t *green, uint16_t *blue) { + + if (do_luminance_correct_) { + *red = CIEMapColor(brightness_, r); + *green = CIEMapColor(brightness_, g); + *blue = CIEMapColor(brightness_, b); + } else { + *red = DirectMapColor(brightness_, r); + *green = DirectMapColor(brightness_, g); + *blue = DirectMapColor(brightness_, b); + } + + if (inverse_color_) { + *red = ~(*red); + *green = ~(*green); + *blue = ~(*blue); + } +} + +void Framebuffer::Fill(uint8_t r, uint8_t g, uint8_t b) { + uint16_t red, green, blue; + MapColors(r, g, b, &red, &green, &blue); + const PixelDesignator &fill = (*shared_mapper_)->GetFillColorBits(); + + for (int bits = kBitPlanes - pwm_bits_; bits < kBitPlanes; ++bits) { + uint16_t mask = 1 << bits; + gpio_bits_t plane_bits = 0; + plane_bits |= ((red & mask) == mask) ? fill.r_bit : 0; + plane_bits |= ((green & mask) == mask) ? fill.g_bit : 0; + plane_bits |= ((blue & mask) == mask) ? fill.b_bit : 0; + + for (int row = 0; row < double_rows_; ++row) { + gpio_bits_t *row_data = ValueAt(row, 0, bits); + for (int col = 0; col < columns_; ++col) { + *row_data++ = plane_bits; + } + } + } +} + +void Framebuffer::SubFill(int x, int y, int width, int height, uint8_t r, uint8_t g, uint8_t b) { + + uint16_t red, green, blue; + MapColors(r, g, b, &red, &green, &blue); + + int safe_y = std::max(0, y); + int safe_y_max = std::min((*shared_mapper_)->height(), y + height); + int safe_x = std::max(0, x); + int safe_x_max = std::min((*shared_mapper_)->width(), x + width); + + for (int row = safe_y; row < safe_y_max; row++) + { + const PixelDesignator* designator = (*shared_mapper_)->get(safe_x, row); + + for (int col = safe_x; col < safe_x_max; col++) + { + if (designator == NULL) continue; + const long pos = designator->gpio_word; + if (pos < 0) continue; // non-used pixel marker. + + gpio_bits_t* bits = bitplane_buffer_ + pos; + const int min_bit_plane = kBitPlanes - pwm_bits_; + bits += (columns_ * min_bit_plane); + const gpio_bits_t r_bits = designator->r_bit; + const gpio_bits_t g_bits = designator->g_bit; + const gpio_bits_t b_bits = designator->b_bit; + const gpio_bits_t designator_mask = designator->mask; + for (uint16_t mask = 1 << min_bit_plane; mask != 1 << kBitPlanes; mask <<= 1) { + gpio_bits_t color_bits = 0; + if (red & mask) color_bits |= r_bits; + if (green & mask) color_bits |= g_bits; + if (blue & mask) color_bits |= b_bits; + *bits = (*bits & designator_mask) | color_bits; + bits += columns_; + } + designator++; + } + } +} + +int Framebuffer::width() const { return (*shared_mapper_)->width(); } +int Framebuffer::height() const { return (*shared_mapper_)->height(); } + +void Framebuffer::SetPixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) { + const PixelDesignator *designator = (*shared_mapper_)->get(x, y); + if (designator == NULL) return; + const long pos = designator->gpio_word; + if (pos < 0) return; // non-used pixel marker. + + uint16_t red, green, blue; + MapColors(r, g, b, &red, &green, &blue); + + gpio_bits_t *bits = bitplane_buffer_ + pos; + const int min_bit_plane = kBitPlanes - pwm_bits_; + bits += (columns_ * min_bit_plane); + const gpio_bits_t r_bits = designator->r_bit; + const gpio_bits_t g_bits = designator->g_bit; + const gpio_bits_t b_bits = designator->b_bit; + const gpio_bits_t designator_mask = designator->mask; + for (uint16_t mask = 1<r, colors->g, colors->b); + ++colors; + } + } +} +// Strange LED-mappings such as RBG or so are handled here. +gpio_bits_t Framebuffer::GetGpioFromLedSequence(char col, + const char *led_sequence, + gpio_bits_t default_r, + gpio_bits_t default_g, + gpio_bits_t default_b) { + const char *pos = strchr(led_sequence, col); + if (pos == NULL) pos = strchr(led_sequence, tolower(col)); + if (pos == NULL) { + fprintf(stderr, "LED sequence '%s' does not contain any '%c'.\n", + led_sequence, col); + abort(); + } + switch (pos - led_sequence) { + case 0: return default_r; + case 1: return default_g; + case 2: return default_b; + } + return default_r; // String too long, should've been caught earlier. +} + +void Framebuffer::InitDefaultDesignator(int x, int y, const char *seq, + PixelDesignator *d) { + const struct HardwareMapping &h = *hardware_mapping_; + gpio_bits_t *bits = ValueAt(y % double_rows_, x, 0); + d->gpio_word = bits - bitplane_buffer_; + d->r_bit = d->g_bit = d->b_bit = 0; + if (y < rows_) { + if (y < double_rows_) { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p0_r1, h.p0_g1, h.p0_b1); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p0_r1, h.p0_g1, h.p0_b1); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p0_r1, h.p0_g1, h.p0_b1); + } else { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p0_r2, h.p0_g2, h.p0_b2); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p0_r2, h.p0_g2, h.p0_b2); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p0_r2, h.p0_g2, h.p0_b2); + } + } + else if (y >= rows_ && y < 2 * rows_) { + if (y - rows_ < double_rows_) { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p1_r1, h.p1_g1, h.p1_b1); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p1_r1, h.p1_g1, h.p1_b1); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p1_r1, h.p1_g1, h.p1_b1); + } else { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p1_r2, h.p1_g2, h.p1_b2); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p1_r2, h.p1_g2, h.p1_b2); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p1_r2, h.p1_g2, h.p1_b2); + } + } + else if (y >= 2*rows_ && y < 3 * rows_) { + if (y - 2*rows_ < double_rows_) { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p2_r1, h.p2_g1, h.p2_b1); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p2_r1, h.p2_g1, h.p2_b1); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p2_r1, h.p2_g1, h.p2_b1); + } else { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p2_r2, h.p2_g2, h.p2_b2); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p2_r2, h.p2_g2, h.p2_b2); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p2_r2, h.p2_g2, h.p2_b2); + } + } + else if (y >= 3*rows_ && y < 4 * rows_) { + if (y - 3*rows_ < double_rows_) { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p3_r1, h.p3_g1, h.p3_b1); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p3_r1, h.p3_g1, h.p3_b1); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p3_r1, h.p3_g1, h.p3_b1); + } else { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p3_r2, h.p3_g2, h.p3_b2); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p3_r2, h.p3_g2, h.p3_b2); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p3_r2, h.p3_g2, h.p3_b2); + } + } + else if (y >= 4*rows_ && y < 5 * rows_){ + if (y - 4*rows_ < double_rows_) { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p4_r1, h.p4_g1, h.p4_b1); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p4_r1, h.p4_g1, h.p4_b1); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p4_r1, h.p4_g1, h.p4_b1); + } else { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p4_r2, h.p4_g2, h.p4_b2); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p4_r2, h.p4_g2, h.p4_b2); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p4_r2, h.p4_g2, h.p4_b2); + } + + } + else { + if (y - 5*rows_ < double_rows_) { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p5_r1, h.p5_g1, h.p5_b1); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p5_r1, h.p5_g1, h.p5_b1); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p5_r1, h.p5_g1, h.p5_b1); + } else { + d->r_bit = GetGpioFromLedSequence('R', seq, h.p5_r2, h.p5_g2, h.p5_b2); + d->g_bit = GetGpioFromLedSequence('G', seq, h.p5_r2, h.p5_g2, h.p5_b2); + d->b_bit = GetGpioFromLedSequence('B', seq, h.p5_r2, h.p5_g2, h.p5_b2); + } + } + + d->mask = ~(d->r_bit | d->g_bit | d->b_bit); +} + +void Framebuffer::Serialize(const char **data, size_t *len) const { + *data = reinterpret_cast(bitplane_buffer_); + *len = buffer_size_; +} + +bool Framebuffer::Deserialize(const char *data, size_t len) { + if (len != buffer_size_) return false; + memcpy(bitplane_buffer_, data, len); + return true; +} + +void Framebuffer::CopyFrom(const Framebuffer *other) { + if (other == this) return; + memcpy(bitplane_buffer_, other->bitplane_buffer_, buffer_size_); +} + +void Framebuffer::DumpToMatrix(GPIO *io, int pwm_low_bit) { + if (Rp1RioIsActive()) { + Rp1RioDumpFramebuffer(this, pwm_low_bit); + return; + } + if (Rp1PioIsActive()) { + Rp1PioDumpFramebuffer(this, pwm_low_bit); + return; + } + + const struct HardwareMapping &h = *hardware_mapping_; + gpio_bits_t color_clk_mask = 0; // Mask of bits while clocking in. + color_clk_mask |= h.p0_r1 | h.p0_g1 | h.p0_b1 | h.p0_r2 | h.p0_g2 | h.p0_b2; + if (parallel_ >= 2) { + color_clk_mask |= h.p1_r1 | h.p1_g1 | h.p1_b1 | h.p1_r2 | h.p1_g2 | h.p1_b2; + } + if (parallel_ >= 3) { + color_clk_mask |= h.p2_r1 | h.p2_g1 | h.p2_b1 | h.p2_r2 | h.p2_g2 | h.p2_b2; + } + if (parallel_ >= 4) { + color_clk_mask |= h.p3_r1 | h.p3_g1 | h.p3_b1 | h.p3_r2 | h.p3_g2 | h.p3_b2; + } + if (parallel_ >= 5) { + color_clk_mask |= h.p4_r1 | h.p4_g1 | h.p4_b1 | h.p4_r2 | h.p4_g2 | h.p4_b2; + } + if (parallel_ >= 6) { + color_clk_mask |= h.p5_r1 | h.p5_g1 | h.p5_b1 | h.p5_r2 | h.p5_g2 | h.p5_b2; + } + + color_clk_mask |= h.clock; + + // Depending if we do dithering, we might not always show the lowest bits. + const int start_bit = std::max(pwm_low_bit, kBitPlanes - pwm_bits_); + + const uint8_t half_double = double_rows_/2; + for (uint8_t row_loop = 0; row_loop < double_rows_; ++row_loop) { + uint8_t d_row; + switch (scan_mode_) { + case 0: // progressive + default: + d_row = row_loop; + break; + + case 1: // interlaced + d_row = ((row_loop < half_double) + ? (row_loop << 1) + : ((row_loop - half_double) << 1) + 1); + } + + // Rows can't be switched very quickly without ghosting, so we do the + // full PWM of one row before switching rows. + for (int b = start_bit; b < kBitPlanes; ++b) { + gpio_bits_t *row_data = ValueAt(d_row, 0, b); + // While the output enable is still on, we can already clock in the next + // data. + for (int col = 0; col < columns_; ++col) { + const gpio_bits_t &out = *row_data++; + io->WriteMaskedBits(out, color_clk_mask); // col + reset clock + io->SetBits(h.clock); // Rising edge: clock color in. + } + io->ClearBits(color_clk_mask); // clock back to normal. + + // OE of the previous row-data must be finished before strobe. + sOutputEnablePulser->WaitPulseFinished(); + + // Setting address and strobing needs to happen in dark time. + row_setter_->SetRowAddress(io, d_row); + + io->SetBits(h.strobe); // Strobe in the previously clocked in row. + io->ClearBits(h.strobe); + + // Now switch on for the sleep time necessary for that bit-plane. + sOutputEnablePulser->SendPulse(b); + } + } +} +} // namespace internal +} // namespace rgb_matrix +namespace rgb_matrix { +namespace internal { + void Framebuffer::ResetGlobals() { + if (sOutputEnablePulser != NULL) { + delete sOutputEnablePulser; + sOutputEnablePulser = NULL; + } + Rp1RioDeinit(); + Rp1PioDeinit(); + if (row_setter_ != NULL) { + delete row_setter_; + row_setter_ = NULL; + } + } +} +} + +extern "C" { + void framebuffer_reset_globals() { + rgb_matrix::internal::Framebuffer::ResetGlobals(); + // Also reset global GPIO bookkeeping in led-matrix's static GPIO object + extern void ledmatrix_reset_global_gpio(); + ledmatrix_reset_global_gpio(); + } +} diff --git a/deps/rpi-rgb-led-matrix/lib/gpio-bits.h b/deps/rpi-rgb-led-matrix/lib/gpio-bits.h new file mode 100644 index 000000000000..de9c7c88c279 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/gpio-bits.h @@ -0,0 +1,28 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see + +// This file needs to compile in C and C++ context, so deliberately broken out. + +#ifndef RPI_GPIOBITS_H +#define RPI_GPIOBITS_H + +#include +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE +typedef uint64_t gpio_bits_t; +#else +typedef uint32_t gpio_bits_t; +#endif + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/gpio.cc b/deps/rpi-rgb-led-matrix/lib/gpio.cc new file mode 100644 index 000000000000..1c627c3ea249 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/gpio.cc @@ -0,0 +1,889 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see + +#define __STDC_FORMAT_MACROS +#include + +#include "gpio.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * nanosleep() takes longer than requested because of OS jitter. + * In about 99.9% of the cases, this is <= 25 microcseconds on + * the Raspberry Pi (empirically determined with a Raspbian kernel), so + * we subtract this value whenever we do nanosleep(); the remaining time + * we then busy wait to get a good accurate result. + * + * You can measure the overhead using DEBUG_SLEEP_JITTER below. + * + * Note: A higher value here will result in more CPU use because of more busy + * waiting inching towards the real value (for all the cases that nanosleep() + * actually was better than this overhead). + * + * This might be interesting to tweak in particular if you have a realtime + * kernel with different characteristics. + */ +#define EMPIRICAL_NANOSLEEP_OVERHEAD_US 12 + +/* + * In case of non-hardware pulse generation, use nanosleep if we want to wait + * longer than these given microseconds beyond the general overhead. + * Below that, just use busy wait. + */ +#define MINIMUM_NANOSLEEP_TIME_US 5 + +/* In order to determine useful values for above, set this to 1 and use the + * hardware pin-pulser. + * It will output a histogram atexit() of how much how often we were over + * the requested time. + * (The full histogram will be shifted by the EMPIRICAL_NANOSLEEP_OVERHEAD_US + * value above. To get a full histogram of OS overhead, set it to 0 first). + */ +#define DEBUG_SLEEP_JITTER 0 + +// Raspberry 1 and 2 have different base addresses for the periphery +#define BCM2708_PERI_BASE 0x20000000 +#define BCM2709_PERI_BASE 0x3F000000 +#define BCM2711_PERI_BASE 0xFE000000 + +#define GPIO_REGISTER_OFFSET 0x200000 +#define COUNTER_1Mhz_REGISTER_OFFSET 0x3000 + +#define GPIO_PWM_BASE_OFFSET (GPIO_REGISTER_OFFSET + 0xC000) +#define GPIO_CLK_BASE_OFFSET 0x101000 + +#define REGISTER_BLOCK_SIZE (4*1024) + +#define PWM_CTL (0x00 / 4) +#define PWM_STA (0x04 / 4) +#define PWM_RNG1 (0x10 / 4) +#define PWM_FIFO (0x18 / 4) + +#define PWM_CTL_CLRF1 (1<<6) // CH1 Clear Fifo (1 Clears FIFO 0 has no effect) +#define PWM_CTL_USEF1 (1<<5) // CH1 Use Fifo (0=data reg transmit 1=Fifo used for transmission) +#define PWM_CTL_POLA1 (1<<4) // CH1 Polarity (0=(0=low 1=high) 1=(1=low 0=high) +#define PWM_CTL_SBIT1 (1<<3) // CH1 Silence Bit (state of output when 0 transmission takes place) +#define PWM_CTL_MODE1 (1<<1) // CH1 Mode (0=pwm 1=serialiser mode) +#define PWM_CTL_PWEN1 (1<<0) // CH1 Enable (0=disable 1=enable) + +#define PWM_STA_EMPT1 (1<<1) +#define PWM_STA_FULL1 (1<<0) + +#define CLK_PASSWD (0x5A<<24) + +#define CLK_CTL_MASH(x)((x)<<9) +#define CLK_CTL_BUSY (1 <<7) +#define CLK_CTL_KILL (1 <<5) +#define CLK_CTL_ENAB (1 <<4) +#define CLK_CTL_SRC(x) ((x)<<0) + +#define CLK_CTL_SRC_PLLD 6 /* 500.0 MHz */ + +#define CLK_DIV_DIVI(x) ((x)<<12) +#define CLK_DIV_DIVF(x) ((x)<< 0) + +#define CLK_PWMCTL 40 +#define CLK_PWMDIV 41 + +// We want to have the last word in the fifo free +#define MAX_PWM_BIT_USE 224 +#define PWM_BASE_TIME_NS 2 + +// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x). +#define INP_GPIO(g) *(s_GPIO_registers+((g)/10)) &= ~(7ull<<(((g)%10)*3)) +#define OUT_GPIO(g) *(s_GPIO_registers+((g)/10)) |= (1ull<<(((g)%10)*3)) + +#define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0 +#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0 + +// We're pre-mapping all the registers on first call of GPIO::Init(), +// so that it is possible to drop privileges afterwards and still have these +// usable. +static volatile uint32_t *s_GPIO_registers = NULL; +static volatile uint32_t *s_Timer1Mhz = NULL; +static volatile uint32_t *s_PWM_registers = NULL; +static volatile uint32_t *s_CLK_registers = NULL; + +namespace rgb_matrix { +static bool LinuxHasModuleLoaded(const char *name) { + FILE *f = fopen("/proc/modules", "r"); + if (f == NULL) return false; // don't care. + char buf[256]; + const size_t namelen = strlen(name); + bool found = false; + while (fgets(buf, sizeof(buf), f) != NULL) { + if (strncmp(buf, name, namelen) == 0) { + found = true; + break; + } + } + fclose(f); + return found; +} + +#define GPIO_BIT(x) (1ull << x) + +GPIO::GPIO() : output_bits_(0), input_bits_(0), reserved_bits_(0), + slowdown_(1) +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + , uses_64_bit_(false) +#endif +{ +} + +gpio_bits_t GPIO::InitOutputs(gpio_bits_t outputs, + bool adafruit_pwm_transition_hack_needed) { + if (s_GPIO_registers == NULL) { + fprintf(stderr, "Attempt to init outputs but not yet Init()-ialized.\n"); + return 0; + } + + // Hack: for the PWM mod, the user soldered together GPIO 18 (new OE) + // with GPIO 4 (old OE). + // Since they are connected inside the HAT, want to make extra sure that, + // whatever the outside system set as pinmux, the old OE is _not_ also + // set as output so that these GPIO outputs don't fight each other. + // + // So explicitly set both of these pins as input initially, so the user + // can switch between the two modes "adafruit-hat" and "adafruit-hat-pwm" + // without trouble. + if (adafruit_pwm_transition_hack_needed) { + INP_GPIO(4); + INP_GPIO(18); + // Even with PWM enabled, GPIO4 still can not be used, because it is + // now connected to the GPIO18 and thus must stay an input. + // So reserve this bit if it is not set in outputs. + reserved_bits_ = GPIO_BIT(4) & ~outputs; + } + + outputs &= ~(output_bits_ | input_bits_ | reserved_bits_); + + // We don't know exactly what GPIO pins are occupied by 1-wire (can we + // easily do that ?), so let's complain only about the default GPIO. + if ((outputs & GPIO_BIT(4)) + && LinuxHasModuleLoaded("w1_gpio")) { + fprintf(stderr, "This Raspberry Pi has the one-wire protocol enabled.\n" + "This will mess with the display if GPIO pins overlap.\n" + "Disable 1-wire in raspi-config (Interface Options).\n\n"); + } + +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + const int kMaxAvailableBit = 45; + uses_64_bit_ |= (outputs >> 32) != 0; +#else + const int kMaxAvailableBit = 31; +#endif + for (int b = 0; b <= kMaxAvailableBit; ++b) { + if (outputs & GPIO_BIT(b)) { + INP_GPIO(b); // for writing, we first need to set as input. + OUT_GPIO(b); + } + } + output_bits_ |= outputs; + return outputs; +} + +void GPIO::ResetState() { + output_bits_ = 0; + input_bits_ = 0; + reserved_bits_ = 0; +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + uses_64_bit_ = false; +#endif +} + +gpio_bits_t GPIO::RequestInputs(gpio_bits_t inputs) { + if (s_GPIO_registers == NULL) { + fprintf(stderr, "Attempt to init inputs but not yet Init()-ialized.\n"); + return 0; + } + + inputs &= ~(output_bits_ | input_bits_ | reserved_bits_); +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + const int kMaxAvailableBit = 45; + uses_64_bit_ |= (inputs >> 32) != 0; +#else + const int kMaxAvailableBit = 31; +#endif + for (int b = 0; b <= kMaxAvailableBit; ++b) { + if (inputs & GPIO_BIT(b)) { + INP_GPIO(b); + } + } + input_bits_ |= inputs; + return inputs; +} + +// We are not interested in the _exact_ model, just good enough to determine +// What to do. +enum RaspberryPiModel { + PI_MODEL_1, + PI_MODEL_2, + PI_MODEL_3, + PI_MODEL_4, + PI_MODEL_5 +}; + +static int ReadBinaryFileToBuffer(uint8_t *buffer, size_t size, + const char *filename) { + const int fd = open(filename, O_RDONLY); + if (fd < 0) return -1; + const ssize_t r = read(fd, buffer, size); // assume one read enough. + close(fd); + return r; +} + +// Like ReadBinaryFileToBuffer(), but adds null-termination. +static int ReadTextFileToBuffer(char *buffer, size_t size, + const char *filename) { + int r = ReadBinaryFileToBuffer((uint8_t *)buffer, size - 1, filename); + buffer[r >= 0 ? r : 0] = '\0'; + return r; +} + +/* + * Try to read the revision from /proc/cpuinfo. In case of any errors, or if + * /proc/cpuinfo simply contains zero as the revision, this function returns + * zero. This is ok because zero was never used as a real revision code. + */ +static uint32_t ReadRevisionFromProcCpuinfo() { + char buffer[4096]; + if (ReadTextFileToBuffer(buffer, sizeof(buffer), "/proc/cpuinfo") < 0) { + fprintf(stderr, "Reading cpuinfo: Could not determine Pi model\n"); + return 0; + } + static const char RevisionTag[] = "Revision"; + const char *revision_key; + if ((revision_key = strstr(buffer, RevisionTag)) == NULL) { + fprintf(stderr, "non-existent Revision: Could not determine Pi model\n"); + return 0; + } + unsigned int pi_revision; + if (sscanf(index(revision_key, ':') + 1, "%x", &pi_revision) != 1) { + return 0; + } + return pi_revision; +} + +// Read a 32-bit big-endian number from a 4-byte buffer. +static uint32_t read_be32(const uint8_t *p) { + return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +} + +// Try to read the revision from the devicetree. +static uint32_t ReadRevisionFromDeviceTree() { + const char *const kDeviceTreeRev = "/proc/device-tree/system/linux,revision"; + uint8_t buffer[4]; + if (ReadBinaryFileToBuffer(buffer, sizeof(buffer), kDeviceTreeRev) != 4) { + fprintf(stderr, "Failed to read revision from %s\n", kDeviceTreeRev); + return 0; + } + return read_be32(buffer); +} + +static RaspberryPiModel DetermineRaspberryModel() { + uint32_t pi_revision = ReadRevisionFromProcCpuinfo(); + if (pi_revision == 0) { + pi_revision = ReadRevisionFromDeviceTree(); + if (pi_revision == 0) { + fprintf(stderr, "Unknown Revision: Could not determine Pi model\n"); + return PI_MODEL_3; // safe guess fallback. + } + } + + // https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-revision-codes + const unsigned pi_type = (pi_revision >> 4) & 0xff; + switch (pi_type) { + case 0x00: /* A */ + case 0x01: /* B, Compute Module 1 */ + case 0x02: /* A+ */ + case 0x03: /* B+ */ + case 0x05: /* Alpha ?*/ + case 0x06: /* Compute Module1 */ + case 0x09: /* Zero */ + case 0x0c: /* Zero W */ + return PI_MODEL_1; + + case 0x04: /* Pi 2 */ + case 0x12: /* Zero W 2 (behaves close to Pi 2) */ + return PI_MODEL_2; + + case 0x11: /* Pi 4 */ + case 0x13: /* Pi 400 */ + case 0x14: /* CM4 */ + case 0x15: /* CM4S */ + return PI_MODEL_4; + + case 0x17: /* Pi 5 */ + case 0x18: /* CM5 */ + case 0x19: /* Pi 500 / 500+ */ + case 0x1a: /* CM5 Lite */ + return PI_MODEL_5; + + default: /* a bunch of versions representing Pi 3 */ + return PI_MODEL_3; + } +} + +static RaspberryPiModel GetPiModel() { + static RaspberryPiModel pi_model = DetermineRaspberryModel(); + return pi_model; +} + +static int GetNumCores() { + return GetPiModel() == PI_MODEL_1 ? 1 : 4; +} + +static uint32_t *mmap_bcm_register(off_t register_offset) { + off_t base = BCM2709_PERI_BASE; // safe fallback guess. + switch (GetPiModel()) { + case PI_MODEL_1: base = BCM2708_PERI_BASE; break; + case PI_MODEL_2: base = BCM2709_PERI_BASE; break; + case PI_MODEL_3: base = BCM2709_PERI_BASE; break; + case PI_MODEL_4: base = BCM2711_PERI_BASE; break; + case PI_MODEL_5: + fprintf(stderr, + "Pi 5-family boards use the RP1 backends instead of the legacy " + "BCM GPIO mapping.\n"); + return NULL; + } + + int mem_fd; + if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { + // Try to fall back to /dev/gpiomem. Unfortunately, that device + // is implemented in a way that it _only_ supports GPIO, not the + // other registers we need, such as PWM or COUNTER_1Mhz, which means + // we only can operate with degraded performance. + // + // But, instead of failing, mmap() then silently succeeds with the + // unsupported offset. So bail out here. + if (register_offset != GPIO_REGISTER_OFFSET) + return NULL; + + mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC); + if (mem_fd < 0) return NULL; + } + + uint32_t *result = + (uint32_t*) mmap(NULL, // Any address in our space will do + REGISTER_BLOCK_SIZE, // Map length + PROT_READ|PROT_WRITE, // Enable r/w on GPIO registers. + MAP_SHARED, + mem_fd, // File to map + base + register_offset // Offset to bcm register + ); + close(mem_fd); + + if (result == MAP_FAILED) { + perror("mmap error: "); + fprintf(stderr, "MMapping from base 0x%lx, offset 0x%lx\n", + base, register_offset); + return NULL; + } + return result; +} + +static bool mmap_all_bcm_registers_once() { + if (s_GPIO_registers != NULL) return true; // already done. + + // The common GPIO registers. + s_GPIO_registers = mmap_bcm_register(GPIO_REGISTER_OFFSET); + if (s_GPIO_registers == NULL) { + return false; + } + + // Time measurement. Might fail when run as non-root. + uint32_t *timereg = mmap_bcm_register(COUNTER_1Mhz_REGISTER_OFFSET); + if (timereg != NULL) { + s_Timer1Mhz = timereg + 1; + } + + // Hardware pin-pulser. Might fail when run as non-root. + s_PWM_registers = mmap_bcm_register(GPIO_PWM_BASE_OFFSET); + s_CLK_registers = mmap_bcm_register(GPIO_CLK_BASE_OFFSET); + + return true; +} + +bool GPIO::Init(int slowdown) { + slowdown_ = slowdown; + + // Pre-mmap all bcm registers we need now and possibly in the future, as to + // allow dropping privileges after GPIO::Init() even as some of these + // registers might be needed later. + if (!mmap_all_bcm_registers_once()) + return false; + + gpio_set_bits_low_ = s_GPIO_registers + (0x1C / sizeof(uint32_t)); + gpio_clr_bits_low_ = s_GPIO_registers + (0x28 / sizeof(uint32_t)); + gpio_read_bits_low_ = s_GPIO_registers + (0x34 / sizeof(uint32_t)); + +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + gpio_set_bits_high_ = s_GPIO_registers + (0x20 / sizeof(uint32_t)); + gpio_clr_bits_high_ = s_GPIO_registers + (0x2C / sizeof(uint32_t)); + gpio_read_bits_high_ = s_GPIO_registers + (0x38 / sizeof(uint32_t)); +#endif + + return true; +} + +bool GPIO::IsPi4() { + return GetPiModel() == PI_MODEL_4; +} + +bool GPIO::IsPi5Family() { + return GetPiModel() == PI_MODEL_5; +} + +/* + * We support also other pinouts that don't have the OE- on the hardware + * PWM output pin, so we need to provide (impefect) 'manual' timing as well. + * Hence all various busy_wait_nano() implementations depending on the hardware. + */ + +// --- PinPulser. Private implementation parts. +namespace { +// Manual timers. +class Timers { +public: + static bool Init(); + static void sleep_nanos(long t); +}; + +// Simplest of PinPulsers. Uses somewhat jittery and manual timers +// to get the timing, but not optimal. +class TimerBasedPinPulser : public PinPulser { +public: + TimerBasedPinPulser(GPIO *io, gpio_bits_t bits, + const std::vector &nano_specs) + : io_(io), bits_(bits), nano_specs_(nano_specs) { + if (!s_Timer1Mhz) { + fprintf(stderr, "FYI: not running as root which means we can't properly " + "control timing unless this is a real-time kernel. Expect color " + "degradation. Consider running as root with sudo.\n"); + } + } + + virtual void SendPulse(int time_spec_number) { + io_->ClearBits(bits_); + Timers::sleep_nanos(nano_specs_[time_spec_number]); + io_->SetBits(bits_); + } + +private: + GPIO *const io_; + const gpio_bits_t bits_; + const std::vector nano_specs_; +}; + +// Check that 3 shows up in isolcpus +static bool HasIsolCPUs() { + char buf[256]; + ReadTextFileToBuffer(buf, sizeof(buf), "/sys/devices/system/cpu/isolated"); + return index(buf, '3') != NULL; +} + +static void busy_wait_nanos_rpi_1(long nanos); +static void busy_wait_nanos_rpi_2(long nanos); +static void busy_wait_nanos_rpi_3(long nanos); +static void busy_wait_nanos_rpi_4(long nanos); +static void (*busy_wait_impl)(long) = busy_wait_nanos_rpi_3; + +// Best effort write to file. Used to set kernel parameters. +static void WriteTo(const char *filename, const char *str) { + const int fd = open(filename, O_WRONLY); + if (fd < 0) return; + if (write(fd, str, strlen(str))) {} // Best effort. Ignore return value. + close(fd); +} + +// By default, the kernel applies some throtteling for realtime +// threads to prevent starvation of non-RT threads. But we +// really want all we can get iff the machine has more cores and +// our RT-thread is locked onto one of these. +// So let's tell it not to do that. +static void DisableRealtimeThrottling() { + if (GetNumCores() == 1) return; // Not safe if we don't have > 1 core. + // We need to leave the kernel a little bit of time, as it does not like + // us to hog the kernel solidly. The default of 950000 leaves 50ms that + // can generate visible flicker, so we reduce that to 10ms. + WriteTo("/proc/sys/kernel/sched_rt_runtime_us", "990000"); +} + +bool Timers::Init() { + if (!mmap_all_bcm_registers_once()) + return false; + + // Choose the busy-wait loop that fits our Pi. + switch (GetPiModel()) { + case PI_MODEL_1: busy_wait_impl = busy_wait_nanos_rpi_1; break; + case PI_MODEL_2: busy_wait_impl = busy_wait_nanos_rpi_2; break; + case PI_MODEL_3: busy_wait_impl = busy_wait_nanos_rpi_3; break; + case PI_MODEL_4: busy_wait_impl = busy_wait_nanos_rpi_4; break; + case PI_MODEL_5: busy_wait_impl = busy_wait_nanos_rpi_4; break; + } + + DisableRealtimeThrottling(); + // If we have it, we run the update thread on core3. No perf-compromises: + WriteTo("/sys/devices/system/cpu/cpu3/cpufreq/scaling_governor", + "performance"); + + if (GetPiModel() != PI_MODEL_1 && !HasIsolCPUs()) { + fprintf(stderr, "Suggestion: to slightly improve display update, add\n\tisolcpus=3\n" + "at the end of /boot/cmdline.txt and reboot (see README.md)\n"); + } + return true; +} + +static uint32_t JitterAllowanceMicroseconds() { + // If this is a Raspberry Pi with more than one core, we add a bit of + // additional overhead measured up to the 99.999%-ile: we can allow to burn + // a bit more busy-wait CPU cycles to get the timing accurate as we have + // more CPU to spare. + switch (GetPiModel()) { + case PI_MODEL_1: + return EMPIRICAL_NANOSLEEP_OVERHEAD_US; // 99.9%-ile + case PI_MODEL_2: case PI_MODEL_3: + return EMPIRICAL_NANOSLEEP_OVERHEAD_US + 35; // 99.999%-ile + case PI_MODEL_4: + case PI_MODEL_5: + return EMPIRICAL_NANOSLEEP_OVERHEAD_US + 10; // this one is fast. + } + return EMPIRICAL_NANOSLEEP_OVERHEAD_US; +} + +void Timers::sleep_nanos(long nanos) { + // For smaller durations, we go straight to busy wait. + + // For larger duration, we use nanosleep() to give the operating system + // a chance to do something else. + + // However, these timings have a lot of jitter, so if we have the 1Mhz timer + // available, we use that to accurately measure time spent and do the + // remaining time with busy wait. If we don't have the timer available + // (not running as root), we just use nanosleep() for larger values. + + if (s_Timer1Mhz) { + static long kJitterAllowanceNanos = JitterAllowanceMicroseconds() * 1000; + if (nanos > kJitterAllowanceNanos + MINIMUM_NANOSLEEP_TIME_US*1000) { + const uint32_t before = *s_Timer1Mhz; + struct timespec sleep_time = { 0, nanos - kJitterAllowanceNanos }; + nanosleep(&sleep_time, NULL); + const uint32_t after = *s_Timer1Mhz; + const long nanoseconds_passed = 1000 * (uint32_t)(after - before); + if (nanoseconds_passed > nanos) { + return; // darn, missed it. + } else { + nanos -= nanoseconds_passed; // remaining time with busy-loop + } + } + } else { + // Not running as root, not having access to 1Mhz timer. Approximate large + // durations with nanosleep(); small durations are done with busy wait. + if (nanos > (EMPIRICAL_NANOSLEEP_OVERHEAD_US + MINIMUM_NANOSLEEP_TIME_US)*1000) { + struct timespec sleep_time + = { 0, nanos - EMPIRICAL_NANOSLEEP_OVERHEAD_US*1000 }; + nanosleep(&sleep_time, NULL); + return; + } + } + + busy_wait_impl(nanos); // Use model-specific busy-loop for remaining time. +} + +static void busy_wait_nanos_rpi_1(long nanos) { + if (nanos < 70) return; + // The following loop is determined empirically on a 700Mhz RPi + for (uint32_t i = (nanos - 70) >> 2; i != 0; --i) { + asm("nop"); + } +} + +static void busy_wait_nanos_rpi_2(long nanos) { + if (nanos < 20) return; + // The following loop is determined empirically on a 900Mhz RPi 2 + for (uint32_t i = (nanos - 20) * 100 / 110; i != 0; --i) { + asm(""); + } +} + +static void busy_wait_nanos_rpi_3(long nanos) { + if (nanos < 20) return; + for (uint32_t i = (nanos - 15) * 100 / 73; i != 0; --i) { + asm(""); + } +} + +static void busy_wait_nanos_rpi_4(long nanos) { + if (nanos < 20) return; + // Interesting, the Pi4 is _slower_ than the Pi3 ? At least for this busy loop + for (uint32_t i = (nanos - 5) * 100 / 132; i != 0; --i) { + asm(""); + } +} + +#if DEBUG_SLEEP_JITTER +static int overshoot_histogram_us[256] = {0}; +static void print_overshoot_histogram() { + fprintf(stderr, "Overshoot histogram >= empirical overhead of %dus\n" + "%6s | %7s | %7s\n", + JitterAllowanceMicroseconds(), "usec", "count", "accum"); + int total_count = 0; + for (int i = 0; i < 256; ++i) total_count += overshoot_histogram_us[i]; + int running_count = 0; + for (int us = 0; us < 256; ++us) { + const int count = overshoot_histogram_us[us]; + if (count > 0) { + running_count += count; + fprintf(stderr, "%s%3dus: %8d %7.3f%%\n", (us == 0) ? "<=" : " +", + us, count, 100.0 * running_count / total_count); + } + } +} +#endif + +// A PinPulser that uses the PWM hardware to create accurate pulses. +// It only works on GPIO-12 or 18 though. +class HardwarePinPulser : public PinPulser { +public: + static bool CanHandle(gpio_bits_t gpio_mask) { +#ifdef DISABLE_HARDWARE_PULSES + return false; +#else + const bool can_handle = gpio_mask==GPIO_BIT(18) || gpio_mask==GPIO_BIT(12); + if (can_handle && (s_PWM_registers == NULL || s_CLK_registers == NULL)) { + // Instead of silently not using the hardware pin pulser and falling back + // to timing based loops, complain loudly and request the user to make + // a choice before continuing. + fprintf(stderr, "Need root. You are configured to use the hardware pulse " + "generator " + "for\n\tsmooth color rendering, however the necessary hardware\n" + "\tregisters can't be accessed because you probably don't run\n" + "\twith root permissions or privileges have been dropped.\n" + "\tSo you either have to run as root (e.g. using sudo) or\n" + "\tsupply the --led-no-hardware-pulse command-line flag.\n\n" + "\tExiting; run as root or with --led-no-hardware-pulse\n\n"); + exit(1); + } + return can_handle; +#endif + } + + HardwarePinPulser(gpio_bits_t pins, const std::vector &specs) + : triggered_(false) { + assert(CanHandle(pins)); + assert(s_CLK_registers && s_PWM_registers && s_Timer1Mhz); + +#if DEBUG_SLEEP_JITTER + atexit(print_overshoot_histogram); +#endif + + if (LinuxHasModuleLoaded("snd_bcm2835")) { + fprintf(stderr, + "\n%s=== snd_bcm2835: found that the Pi sound module is loaded. ===%s\n" + "Don't use the built-in sound of the Pi together with this lib; it is known to be\n" + "incompatible and cause trouble and hangs (you can still use external USB sound adapters).\n\n" + "See Troubleshooting section in README how to disable the sound module.\n" + "You can also run with --led-no-hardware-pulse to avoid the incompatibility,\n" + "but you will have more flicker.\n" + "Exiting; fix the above first or use --led-no-hardware-pulse\n\n", + "\033[1;31m", "\033[0m"); + exit(1); + } + + for (size_t i = 0; i < specs.size(); ++i) { + // Hints how long to nanosleep, already corrected for system overhead. + sleep_hints_us_.push_back(specs[i]/1000 - JitterAllowanceMicroseconds()); + } + + const int base = specs[0]; + // Get relevant registers + fifo_ = s_PWM_registers + PWM_FIFO; + + if (pins == GPIO_BIT(18)) { + // set GPIO 18 to PWM0 mode (Alternative 5) + SetGPIOMode(s_GPIO_registers, 18, 2); + } else if (pins == GPIO_BIT(12)) { + // set GPIO 12 to PWM0 mode (Alternative 0) + SetGPIOMode(s_GPIO_registers, 12, 4); + } else { + assert(false); // should've been caught by CanHandle() + } + InitPWMDivider((base/2) / PWM_BASE_TIME_NS); + for (size_t i = 0; i < specs.size(); ++i) { + pwm_range_.push_back(2 * specs[i] / base); + } + } + + virtual void SendPulse(int c) { + if (pwm_range_[c] < 16) { + s_PWM_registers[PWM_RNG1] = pwm_range_[c]; + + *fifo_ = pwm_range_[c]; + } else { + // Keep the actual range as short as possible, as we have to + // wait for one full period of these in the zero phase. + // The hardware can't deal with values < 2, so only do this when + // have enough of these. + s_PWM_registers[PWM_RNG1] = pwm_range_[c] / 8; + + *fifo_ = pwm_range_[c] / 8; + *fifo_ = pwm_range_[c] / 8; + *fifo_ = pwm_range_[c] / 8; + *fifo_ = pwm_range_[c] / 8; + *fifo_ = pwm_range_[c] / 8; + *fifo_ = pwm_range_[c] / 8; + *fifo_ = pwm_range_[c] / 8; + *fifo_ = pwm_range_[c] / 8; + } + + /* + * We need one value at the end to have it go back to + * default state (otherwise it just repeats the last + * value, so will be constantly 'on'). + */ + *fifo_ = 0; // sentinel. + + /* + * For some reason, we need a second empty sentinel in the + * fifo, otherwise our way to detect the end of the pulse, + * which relies on 'is the queue empty' does not work. It is + * not entirely clear why that is from the datasheet, + * but probably there is some buffering register in which data + * elements are kept after the fifo is emptied. + */ + *fifo_ = 0; + + sleep_hint_us_ = sleep_hints_us_[c]; + start_time_ = *s_Timer1Mhz; + triggered_ = true; + s_PWM_registers[PWM_CTL] = PWM_CTL_USEF1 | PWM_CTL_PWEN1 | PWM_CTL_POLA1; + } + + virtual void WaitPulseFinished() { + if (!triggered_) return; + // Determine how long we already spent and sleep to get close to the + // actual end-time of our sleep period. + // + // TODO(hzeller): find if it is possible to get some sort of interrupt from + // the hardware once it is done with the pulse. Sounds silly that there is + // not (so far, only tested GPIO interrupt with a feedback line, but that + // is super-slow with 20μs overhead). + if (sleep_hint_us_ > 0) { + const uint32_t already_elapsed_usec = *s_Timer1Mhz - start_time_; + const int to_sleep_us = sleep_hint_us_ - already_elapsed_usec; + if (to_sleep_us > 0) { + struct timespec sleep_time = { 0, 1000 * to_sleep_us }; + nanosleep(&sleep_time, NULL); + +#if DEBUG_SLEEP_JITTER + { + // Record histogram of realtime jitter how much longer we actually + // took. + const int total_us = *s_Timer1Mhz - start_time_; + const int nanoslept_us = total_us - already_elapsed_usec; + int overshoot = nanoslept_us - (to_sleep_us + JitterAllowanceMicroseconds()); + if (overshoot < 0) overshoot = 0; + if (overshoot > 255) overshoot = 255; + overshoot_histogram_us[overshoot]++; + } +#endif + } + } + + while ((s_PWM_registers[PWM_STA] & PWM_STA_EMPT1) == 0) { + // busy wait until done. + } + s_PWM_registers[PWM_CTL] = PWM_CTL_USEF1 | PWM_CTL_POLA1 | PWM_CTL_CLRF1; + triggered_ = false; + } + +private: + void SetGPIOMode(volatile uint32_t *gpioReg, unsigned gpio, unsigned mode) { + const int reg = gpio / 10; + const int mode_pos = (gpio % 10) * 3; + gpioReg[reg] = (gpioReg[reg] & ~(7 << mode_pos)) | (mode << mode_pos); + } + + void InitPWMDivider(uint32_t divider) { + assert(divider < (1<<12)); // we only have 12 bits. + + s_PWM_registers[PWM_CTL] = PWM_CTL_USEF1 | PWM_CTL_POLA1 | PWM_CTL_CLRF1; + + // reset PWM clock + s_CLK_registers[CLK_PWMCTL] = CLK_PASSWD | CLK_CTL_KILL; + + // set PWM clock source as 500 MHz PLLD + s_CLK_registers[CLK_PWMCTL] = CLK_PASSWD | CLK_CTL_SRC(CLK_CTL_SRC_PLLD); + + // set PWM clock divider + s_CLK_registers[CLK_PWMDIV] + = CLK_PASSWD | CLK_DIV_DIVI(divider) | CLK_DIV_DIVF(0); + + // enable PWM clock + s_CLK_registers[CLK_PWMCTL] + = CLK_PASSWD | CLK_CTL_ENAB | CLK_CTL_SRC(CLK_CTL_SRC_PLLD); + } + +private: + std::vector pwm_range_; + std::vector sleep_hints_us_; + volatile uint32_t *fifo_; + uint32_t start_time_; + int sleep_hint_us_; + bool triggered_; +}; + +} // end anonymous namespace + +// Public PinPulser factory +PinPulser *PinPulser::Create(GPIO *io, gpio_bits_t gpio_mask, + bool allow_hardware_pulsing, + const std::vector &nano_wait_spec) { + if (!Timers::Init()) return NULL; + if (allow_hardware_pulsing && HardwarePinPulser::CanHandle(gpio_mask)) { + return new HardwarePinPulser(gpio_mask, nano_wait_spec); + } else { + return new TimerBasedPinPulser(io, gpio_mask, nano_wait_spec); + } +} + +// For external use, e.g. in the matrix for extra time. +uint32_t GetMicrosecondCounter() { + if (s_Timer1Mhz) return *s_Timer1Mhz; + + // When run as non-root, we can't read the timer. Fall back to slow + // operating-system ways. + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + const uint64_t micros = ts.tv_nsec / 1000; + const uint64_t epoch_usec = (uint64_t)ts.tv_sec * 1000000 + micros; + return epoch_usec & 0xFFFFFFFF; +} + +// For external use, e.g. to lessen busy waiting. +void SleepMicroseconds(long t) { + Timers::sleep_nanos(t * 1000); +} + +} // namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/gpio.h b/deps/rpi-rgb-led-matrix/lib/gpio.h new file mode 100644 index 000000000000..da6204a9424c --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/gpio.h @@ -0,0 +1,174 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see + +#ifndef RPI_GPIO_INTERNAL_H +#define RPI_GPIO_INTERNAL_H + +#include "gpio-bits.h" + +#include + +#if __ARM_ARCH >= 7 +#define LED_MATRIX_ALLOW_BARRIER_DELAY 1 +#else +#define LED_MATRIX_ALLOW_BARRIER_DELAY 0 +#endif + +// Putting this in our namespace to not collide with other things called like +// this. +namespace rgb_matrix { +// For now, everything is initialized as output. +class GPIO { +public: + GPIO(); + + // Initialize before use. Returns 'true' if successful, 'false' otherwise + // (e.g. due to a permission problem). + bool Init(int slowdown); + + // Initialize outputs. + // Returns the bits that were available and could be set for output. + // (never use the optional adafruit_hack_needed parameter, it is used + // internally to this library). + gpio_bits_t InitOutputs(gpio_bits_t outputs, + bool adafruit_hack_needed = false); + + // Request given bitmap of GPIO inputs. + // Returns the bits that were available and could be reserved. + gpio_bits_t RequestInputs(gpio_bits_t inputs); + + // Reset internal bookkeeping about which GPIOs have been claimed. + // This does not touch the hardware registers directly; it clears the + // tracked masks so subsequent InitOutputs/RequestInputs will reconfigure + // GPIO pins as needed. + void ResetState(); + + // Set the bits that are '1' in the output. Leave the rest untouched. + inline void SetBits(gpio_bits_t value) { + if (!value) return; + WriteSetBits(value); + delay(); + } + + // Clear the bits that are '1' in the output. Leave the rest untouched. + inline void ClearBits(gpio_bits_t value) { + if (!value) return; + WriteClrBits(value); + delay(); + } + + // Write all the bits of "value" mentioned in "mask". Leave the rest untouched. + inline void WriteMaskedBits(gpio_bits_t value, gpio_bits_t mask) { + // Writing a word is two operations. The IO is actually pretty slow, so + // this should probably be unnoticable. + WriteClrBits(~value & mask); + WriteSetBits(value & mask); + delay(); + } + + inline gpio_bits_t Read() const { return ReadRegisters() & input_bits_; } + + // Return if this appears to be a Pi 4-class board. + static bool IsPi4(); + // Return if this appears to be a Pi 5-family board with RP1 I/O. + static bool IsPi5Family(); + +private: + inline void delay() const { +#if LED_MATRIX_ALLOW_BARRIER_DELAY + if (slowdown_ == -1) { + asm volatile("dsb\tst"); + return; + } +#endif + for (int n = 0; n < slowdown_; n++) { + *gpio_clr_bits_low_ = 0; + } + } + + inline gpio_bits_t ReadRegisters() const { + return (static_cast(*gpio_read_bits_low_) +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + | (static_cast(*gpio_read_bits_low_) << 32) +#endif + ); + } + + inline void WriteSetBits(gpio_bits_t value) { + *gpio_set_bits_low_ = static_cast(value & 0xFFFFFFFF); +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + if (uses_64_bit_) + *gpio_set_bits_high_ = static_cast(value >> 32); +#endif + } + + inline void WriteClrBits(gpio_bits_t value) { + *gpio_clr_bits_low_ = static_cast(value & 0xFFFFFFFF); +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + if (uses_64_bit_) + *gpio_clr_bits_high_ = static_cast(value >> 32); +#endif + } + +private: + gpio_bits_t output_bits_; + gpio_bits_t input_bits_; + gpio_bits_t reserved_bits_; + int slowdown_; + + volatile uint32_t *gpio_set_bits_low_; + volatile uint32_t *gpio_clr_bits_low_; + volatile uint32_t *gpio_read_bits_low_; + +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + bool uses_64_bit_; + volatile uint32_t *gpio_set_bits_high_; + volatile uint32_t *gpio_clr_bits_high_; + volatile uint32_t *gpio_read_bits_high_; +#endif +}; + +// A PinPulser is a utility class that pulses a GPIO pin. There can be various +// implementations. +class PinPulser { +public: + // Factory for a PinPulser. Chooses the right implementation depending + // on the context (CPU and which pins are affected). + // "gpio_mask" is the mask that should be output (since we only + // need negative pulses, this is what it does) + // "nano_wait_spec" contains a list of time periods we'd like + // invoke later. This can be used to pre-process timings if needed. + static PinPulser *Create(GPIO *io, gpio_bits_t gpio_mask, + bool allow_hardware_pulsing, + const std::vector &nano_wait_spec); + + virtual ~PinPulser() {} + + // Send a pulse with a given length (index into nano_wait_spec array). + virtual void SendPulse(int time_spec_number) = 0; + + // If SendPulse() is asynchronously implemented, wait for pulse to finish. + virtual void WaitPulseFinished() {} +}; + +// Get rolling over microsecond counter. We get this from a hardware register +// if possible and a terrible slow fallback otherwise. +uint32_t GetMicrosecondCounter(); + +void SleepMicroseconds(long); + +} // end namespace rgb_matrix + +#endif // RPI_GPIO_INGERNALH diff --git a/deps/rpi-rgb-led-matrix/lib/graphics.cc b/deps/rpi-rgb-led-matrix/lib/graphics.cc new file mode 100644 index 000000000000..9e88961a2372 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/graphics.cc @@ -0,0 +1,185 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2014 Henner Zeller +// +// 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. +// +// 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, see + +#include "graphics.h" +#include "utf8-internal.h" + +#include +#include +#include + +namespace rgb_matrix { +bool SetImage(Canvas *c, int canvas_offset_x, int canvas_offset_y, + const uint8_t *buffer, size_t size, + const int width, const int height, + bool is_bgr) { + if (3 * width * height != (int)size) // Sanity check + return false; + + int image_display_w = width; + int image_display_h = height; + + size_t skip_start_row = 0; // Bytes to skip before each row + if (canvas_offset_x < 0) { + skip_start_row = -canvas_offset_x * 3; + image_display_w += canvas_offset_x; + if (image_display_w <= 0) return false; // Done. outside canvas. + canvas_offset_x = 0; + } + if (canvas_offset_y < 0) { + // Skip buffer to the first row we'll be showing + buffer += 3 * width * -canvas_offset_y; + image_display_h += canvas_offset_y; + if (image_display_h <= 0) return false; // Done. outside canvas. + canvas_offset_y = 0; + } + const int w = std::min(c->width(), canvas_offset_x + image_display_w); + const int h = std::min(c->height(), canvas_offset_y + image_display_h); + + // Bytes to skip for wider than canvas image at the end of a row + const size_t skip_end_row = (canvas_offset_x + image_display_w > w) + ? (canvas_offset_x + image_display_w - w) * 3 + : 0; + + // Let's make this a combined skip per row and adjust where we start. + const size_t next_row_skip = skip_start_row + skip_end_row; + buffer += skip_start_row; + + if (is_bgr) { + for (int y = canvas_offset_y; y < h; ++y) { + for (int x = canvas_offset_x; x < w; ++x) { + c->SetPixel(x, y, buffer[2], buffer[1], buffer[0]); + buffer += 3; + } + buffer += next_row_skip; + } + } else { + for (int y = canvas_offset_y; y < h; ++y) { + for (int x = canvas_offset_x; x < w; ++x) { + c->SetPixel(x, y, buffer[0], buffer[1], buffer[2]); + buffer += 3; + } + buffer += next_row_skip; + } + } + return true; +} + +int DrawText(Canvas *c, const Font &font, + int x, int y, const Color &color, + const char *utf8_text) { + return DrawText(c, font, x, y, color, NULL, utf8_text); +} + +int DrawText(Canvas *c, const Font &font, + int x, int y, const Color &color, const Color *background_color, + const char *utf8_text, int extra_spacing) { + const int start_x = x; + while (*utf8_text) { + const uint32_t cp = utf8_next_codepoint(utf8_text); + x += font.DrawGlyph(c, x, y, color, background_color, cp); + x += extra_spacing; + } + return x - start_x; +} + +// There used to be a symbol without the optional extra_spacing parameter. Let's +// define this here so that people linking against an old library will still +// have their code usable. Now: 2017-06-04; can probably be removed in a couple +// of months. +int DrawText(Canvas *c, const Font &font, + int x, int y, const Color &color, const Color *background_color, + const char *utf8_text) { + return DrawText(c, font, x, y, color, background_color, utf8_text, 0); +} + +int VerticalDrawText(Canvas *c, const Font &font, int x, int y, + const Color &color, const Color *background_color, + const char *utf8_text, int extra_spacing) { + const int start_y = y; + while (*utf8_text) { + const uint32_t cp = utf8_next_codepoint(utf8_text); + font.DrawGlyph(c, x, y, color, background_color, cp); + y += font.height() + extra_spacing; + } + return y - start_y; +} + +int MeasureText(const Font &font, const char *utf8_text, int kerning_offset) { + int result = 0; + while (*utf8_text) { + const uint32_t cp = utf8_next_codepoint(utf8_text); + int width = font.CharacterWidth(cp); + if (width > 0) + result += width; + if (*utf8_text) + result += kerning_offset; + } + return result; +} + +void DrawCircle(Canvas *c, int x0, int y0, int radius, const Color &color) { + int x = radius, y = 0; + int radiusError = 1 - x; + + while (y <= x) { + c->SetPixel(x + x0, y + y0, color.r, color.g, color.b); + c->SetPixel(y + x0, x + y0, color.r, color.g, color.b); + c->SetPixel(-x + x0, y + y0, color.r, color.g, color.b); + c->SetPixel(-y + x0, x + y0, color.r, color.g, color.b); + c->SetPixel(-x + x0, -y + y0, color.r, color.g, color.b); + c->SetPixel(-y + x0, -x + y0, color.r, color.g, color.b); + c->SetPixel(x + x0, -y + y0, color.r, color.g, color.b); + c->SetPixel(y + x0, -x + y0, color.r, color.g, color.b); + y++; + if (radiusError<0){ + radiusError += 2 * y + 1; + } else { + x--; + radiusError+= 2 * (y - x + 1); + } + } +} + +void DrawLine(Canvas *c, int x0, int y0, int x1, int y1, const Color &color) { + int dy = y1 - y0, dx = x1 - x0, gradient, x, y, shift = 0x10; + + if (abs(dx) > abs(dy)) { + // x variation is bigger than y variation + if (x1 < x0) { + std::swap(x0, x1); + std::swap(y0, y1); + } + gradient = (dy << shift) / dx ; + + for (x = x0 , y = 0x8000 + (y0 << shift); x <= x1; ++x, y += gradient) { + c->SetPixel(x, y >> shift, color.r, color.g, color.b); + } + } else if (dy != 0) { + // y variation is bigger than x variation + if (y1 < y0) { + std::swap(x0, x1); + std::swap(y0, y1); + } + gradient = (dx << shift) / dy; + for (y = y0 , x = 0x8000 + (x0 << shift); y <= y1; ++y, x += gradient) { + c->SetPixel(x >> shift, y, color.r, color.g, color.b); + } + } else { + c->SetPixel(x0, y0, color.r, color.g, color.b); + } +} + +}//namespace diff --git a/deps/rpi-rgb-led-matrix/lib/hardware-mapping.c b/deps/rpi-rgb-led-matrix/lib/hardware-mapping.c new file mode 100644 index 000000000000..758b42868af7 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/hardware-mapping.c @@ -0,0 +1,287 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- + * Copyright (C) 2013, 2016 Henner Zeller + * + * 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. + * + * 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, see + */ + +/* + * We do this in plain C so that we can use designated initializers. + */ +#include "hardware-mapping.h" + +#define GPIO_BIT(b) ((uint64_t)1<<(b)) + +struct HardwareMapping matrix_hardware_mappings[] = { + /* + * The regular hardware mapping described in the wiring.md and used + * by the adapter PCBs. + */ + { + .name = "regular", + + .output_enable = GPIO_BIT(18), + .clock = GPIO_BIT(17), + .strobe = GPIO_BIT(4), + + /* Address lines */ + .a = GPIO_BIT(22), + .b = GPIO_BIT(23), + .c = GPIO_BIT(24), + .d = GPIO_BIT(25), + .e = GPIO_BIT(15), /* RxD kept free unless 1:64 */ + + /* Parallel chain 0, RGB for both sub-panels */ + .p0_r1 = GPIO_BIT(11), /* masks: SPI0_SCKL */ + .p0_g1 = GPIO_BIT(27), /* Not on RPi1, Rev1; use "regular-pi1" instead */ + .p0_b1 = GPIO_BIT(7), /* masks: SPI0_CE1 */ + .p0_r2 = GPIO_BIT(8), /* masks: SPI0_CE0 */ + .p0_g2 = GPIO_BIT(9), /* masks: SPI0_MISO */ + .p0_b2 = GPIO_BIT(10), /* masks: SPI0_MOSI */ + + /* All the following are only available with 40 GPIP pins, on A+/B+/Pi2,3 */ + /* Chain 1 */ + .p1_r1 = GPIO_BIT(12), + .p1_g1 = GPIO_BIT(5), + .p1_b1 = GPIO_BIT(6), + .p1_r2 = GPIO_BIT(19), + .p1_g2 = GPIO_BIT(13), + .p1_b2 = GPIO_BIT(20), + + /* Chain 2 */ + .p2_r1 = GPIO_BIT(14), /* masks TxD when parallel=3 */ + .p2_g1 = GPIO_BIT(2), /* masks SCL when parallel=3 */ + .p2_b1 = GPIO_BIT(3), /* masks SDA when parallel=3 */ + .p2_r2 = GPIO_BIT(26), + .p2_g2 = GPIO_BIT(16), + .p2_b2 = GPIO_BIT(21), + }, + + /* + * This is used if you have an Adafruit HAT in the default configuration + */ + { + .name = "adafruit-hat", + + .output_enable = GPIO_BIT(4), + .clock = GPIO_BIT(17), + .strobe = GPIO_BIT(21), + + .a = GPIO_BIT(22), + .b = GPIO_BIT(26), + .c = GPIO_BIT(27), + .d = GPIO_BIT(20), + .e = GPIO_BIT(24), /* Needs manual wiring, see README.md */ + + .p0_r1 = GPIO_BIT(5), + .p0_g1 = GPIO_BIT(13), + .p0_b1 = GPIO_BIT(6), + .p0_r2 = GPIO_BIT(12), + .p0_g2 = GPIO_BIT(16), + .p0_b2 = GPIO_BIT(23), + }, + + /* + * An Adafruit HAT with the PWM modification + */ + { + .name = "adafruit-hat-pwm", + + .output_enable = GPIO_BIT(18), /* The only change compared to above */ + .clock = GPIO_BIT(17), + .strobe = GPIO_BIT(21), + + .a = GPIO_BIT(22), + .b = GPIO_BIT(26), + .c = GPIO_BIT(27), + .d = GPIO_BIT(20), + .e = GPIO_BIT(24), + + .p0_r1 = GPIO_BIT(5), + .p0_g1 = GPIO_BIT(13), + .p0_b1 = GPIO_BIT(6), + .p0_r2 = GPIO_BIT(12), + .p0_g2 = GPIO_BIT(16), + .p0_b2 = GPIO_BIT(23), + }, + + /* + * The regular pin-out, but for Raspberry Pi1. The very first Pi1 Rev1 uses + * the same pin for GPIO-21 as later Pis use GPIO-27. Make it work for both. + */ + { + .name = "regular-pi1", + + .output_enable = GPIO_BIT(18), + .clock = GPIO_BIT(17), + .strobe = GPIO_BIT(4), + + /* Address lines */ + .a = GPIO_BIT(22), + .b = GPIO_BIT(23), + .c = GPIO_BIT(24), + .d = GPIO_BIT(25), + .e = GPIO_BIT(15), /* RxD kept free unless 1:64 */ + + /* Parallel chain 0, RGB for both sub-panels */ + .p0_r1 = GPIO_BIT(11), /* masks: SPI0_SCKL */ + /* On Pi1 Rev1, the pin other Pis have GPIO27, these have GPIO21. So make + * this work for both Rev1 and Rev2. + */ + .p0_g1 = GPIO_BIT(21) | GPIO_BIT(27), + .p0_b1 = GPIO_BIT(7), /* masks: SPI0_CE1 */ + .p0_r2 = GPIO_BIT(8), /* masks: SPI0_CE0 */ + .p0_g2 = GPIO_BIT(9), /* masks: SPI0_MISO */ + .p0_b2 = GPIO_BIT(10), /* masks: SPI0_MOSI */ + + /* No more chains - there are not enough GPIO */ + }, + + /* + * Classic: Early forms of this library had this as default mapping, mostly + * derived from the 26 GPIO-header version so that it also can work + * on 40 Pin GPIO headers with more parallel chains. + * Not used anymore. + */ + { + .name = "classic", + + .output_enable = GPIO_BIT(27), /* Not available on RPi1, Rev 1 */ + .clock = GPIO_BIT(11), + .strobe = GPIO_BIT(4), + + .a = GPIO_BIT(7), + .b = GPIO_BIT(8), + .c = GPIO_BIT(9), + .d = GPIO_BIT(10), + + .p0_r1 = GPIO_BIT(17), + .p0_g1 = GPIO_BIT(18), + .p0_b1 = GPIO_BIT(22), + .p0_r2 = GPIO_BIT(23), + .p0_g2 = GPIO_BIT(24), + .p0_b2 = GPIO_BIT(25), + + .p1_r1 = GPIO_BIT(12), + .p1_g1 = GPIO_BIT(5), + .p1_b1 = GPIO_BIT(6), + .p1_r2 = GPIO_BIT(19), + .p1_g2 = GPIO_BIT(13), + .p1_b2 = GPIO_BIT(20), + + .p2_r1 = GPIO_BIT(14), /* masks TxD if parallel = 3 */ + .p2_g1 = GPIO_BIT(2), /* masks SDA if parallel = 3 */ + .p2_b1 = GPIO_BIT(3), /* masks SCL if parallel = 3 */ + .p2_r2 = GPIO_BIT(15), + .p2_g2 = GPIO_BIT(26), + .p2_b2 = GPIO_BIT(21), + }, + + /* + * Classic pin-out for Rev-A Raspberry Pi. + */ + { + .name = "classic-pi1", + + /* The Revision-1 and Revision-2 boards have different GPIO mappings + * on the P1-3 and P1-5. So we use both interpretations. + * To keep the I2C pins free, we avoid these in later mappings. + */ + .output_enable = GPIO_BIT(0) | GPIO_BIT(2), + .clock = GPIO_BIT(1) | GPIO_BIT(3), + .strobe = GPIO_BIT(4), + + .a = GPIO_BIT(7), + .b = GPIO_BIT(8), + .c = GPIO_BIT(9), + .d = GPIO_BIT(10), + + .p0_r1 = GPIO_BIT(17), + .p0_g1 = GPIO_BIT(18), + .p0_b1 = GPIO_BIT(22), + .p0_r2 = GPIO_BIT(23), + .p0_g2 = GPIO_BIT(24), + .p0_b2 = GPIO_BIT(25), + }, + +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + /* + * Custom pin-out for compute-module + */ + { + .name = "compute-module", + + /* This GPIO mapping is made for the official I/O development + * board. No pin is left free when using 6 parallel chains. + */ + .output_enable = GPIO_BIT(18), + .clock = GPIO_BIT(16), + .strobe = GPIO_BIT(17), + + .a = GPIO_BIT(2), + .b = GPIO_BIT(3), + .c = GPIO_BIT(4), + .d = GPIO_BIT(5), + .e = GPIO_BIT(6), /* RxD kept free unless 1:64 */ + + /* Chain 0 */ + .p0_r1 = GPIO_BIT(7), + .p0_g1 = GPIO_BIT(8), + .p0_b1 = GPIO_BIT(9), + .p0_r2 = GPIO_BIT(10), + .p0_g2 = GPIO_BIT(11), + .p0_b2 = GPIO_BIT(12), + + /* Chain 1 */ + .p1_r1 = GPIO_BIT(13), + .p1_g1 = GPIO_BIT(14), + .p1_b1 = GPIO_BIT(15), + .p1_r2 = GPIO_BIT(19), + .p1_g2 = GPIO_BIT(20), + .p1_b2 = GPIO_BIT(21), + + /* Chain 2 */ + .p2_r1 = GPIO_BIT(22), + .p2_g1 = GPIO_BIT(23), + .p2_b1 = GPIO_BIT(24), + .p2_r2 = GPIO_BIT(25), + .p2_g2 = GPIO_BIT(26), + .p2_b2 = GPIO_BIT(27), + + /* Chain 3 */ + .p3_r1 = GPIO_BIT(28), + .p3_g1 = GPIO_BIT(29), + .p3_b1 = GPIO_BIT(30), + .p3_r2 = GPIO_BIT(31), + .p3_g2 = GPIO_BIT(32), + .p3_b2 = GPIO_BIT(33), + + /* Chain 4 */ + .p4_r1 = GPIO_BIT(34), + .p4_g1 = GPIO_BIT(35), + .p4_b1 = GPIO_BIT(36), + .p4_r2 = GPIO_BIT(37), + .p4_g2 = GPIO_BIT(38), + .p4_b2 = GPIO_BIT(39), + + /* Chain 5 */ + .p5_r1 = GPIO_BIT(40), + .p5_g1 = GPIO_BIT(41), + .p5_b1 = GPIO_BIT(42), + .p5_r2 = GPIO_BIT(43), + .p5_g2 = GPIO_BIT(44), + .p5_b2 = GPIO_BIT(45), + }, +#endif + + {0} +}; diff --git a/deps/rpi-rgb-led-matrix/lib/hardware-mapping.h b/deps/rpi-rgb-led-matrix/lib/hardware-mapping.h new file mode 100644 index 000000000000..df4b4405c69a --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/hardware-mapping.h @@ -0,0 +1,60 @@ +/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*- + * Copyright (C) 2013 Henner Zeller + * + * 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. + * + * 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, see + */ +#ifndef RPI_HARDWARE_MAPPING_H +#define RPI_HARDWARE_MAPPING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "gpio-bits.h" + +struct HardwareMapping { + const char *name; + int max_parallel_chains; + + gpio_bits_t output_enable; + gpio_bits_t clock; + gpio_bits_t strobe; + + gpio_bits_t a, b, c, d, e; + + gpio_bits_t p0_r1, p0_g1, p0_b1; + gpio_bits_t p0_r2, p0_g2, p0_b2; + + gpio_bits_t p1_r1, p1_g1, p1_b1; + gpio_bits_t p1_r2, p1_g2, p1_b2; + + gpio_bits_t p2_r1, p2_g1, p2_b1; + gpio_bits_t p2_r2, p2_g2, p2_b2; + + gpio_bits_t p3_r1, p3_g1, p3_b1; + gpio_bits_t p3_r2, p3_g2, p3_b2; + + gpio_bits_t p4_r1, p4_g1, p4_b1; + gpio_bits_t p4_r2, p4_g2, p4_b2; + + gpio_bits_t p5_r1, p5_g1, p5_b1; + gpio_bits_t p5_r2, p5_g2, p5_b2; +}; + +extern struct HardwareMapping matrix_hardware_mappings[]; + +#ifdef __cplusplus +} // extern C +#endif + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/led-matrix-c.cc b/deps/rpi-rgb-led-matrix/lib/led-matrix-c.cc new file mode 100644 index 000000000000..5f12e9b89a48 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/led-matrix-c.cc @@ -0,0 +1,327 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see +// +// C-bridge for led matrix. +#include "led-matrix-c.h" + +#include +#include + +#include "led-matrix.h" +#include "graphics.h" + +// Make sure C++ is in sync with C +static_assert(sizeof(rgb_matrix::RGBMatrix::Options) == sizeof(RGBLedMatrixOptions), "C and C++ out of sync"); +static_assert(sizeof(rgb_matrix::RuntimeOptions) == sizeof(RGBLedRuntimeOptions), "C and C++ out of sync"); + +// Our opaque dummy structs to communicate with the c-world +struct RGBLedMatrix {}; +struct LedCanvas {}; +struct LedFont {}; + + +static rgb_matrix::RGBMatrix *to_matrix(struct RGBLedMatrix *matrix) { + return reinterpret_cast(matrix); +} +static struct RGBLedMatrix *from_matrix(rgb_matrix::RGBMatrix *matrix) { + return reinterpret_cast(matrix); +} + +static rgb_matrix::FrameCanvas *to_canvas(struct LedCanvas *canvas) { + return reinterpret_cast(canvas); +} +static struct LedCanvas *from_canvas(rgb_matrix::FrameCanvas *canvas) { + return reinterpret_cast(canvas); +} + +static rgb_matrix::Font *to_font(struct LedFont *font) { + return reinterpret_cast(font); +} +static struct LedFont *from_font(rgb_matrix::Font *font) { + return reinterpret_cast(font); +} +static rgb_matrix::Color* to_color(struct Color* color) { + return reinterpret_cast(color); +} + + +static struct RGBLedMatrix *led_matrix_create_from_options_optional_edit( + struct RGBLedMatrixOptions *opts, struct RGBLedRuntimeOptions *rt_opts, + int *argc, char ***argv, bool remove_consumed_flags) { + rgb_matrix::RuntimeOptions default_rt; + rgb_matrix::RGBMatrix::Options default_opts; + + if (opts) { + // Copy between C struct and C++ struct. The C++ struct already has a + // default constructor that sets some values. These we override with the + // C-struct values if available. + // We assume everything non-zero has an explicit value. +#define OPT_COPY_IF_SET(o) if (opts->o) default_opts.o = opts->o + OPT_COPY_IF_SET(hardware_mapping); + OPT_COPY_IF_SET(rows); + OPT_COPY_IF_SET(cols); + OPT_COPY_IF_SET(chain_length); + OPT_COPY_IF_SET(parallel); + OPT_COPY_IF_SET(pwm_bits); + OPT_COPY_IF_SET(pwm_lsb_nanoseconds); + OPT_COPY_IF_SET(pwm_dither_bits); + OPT_COPY_IF_SET(brightness); + OPT_COPY_IF_SET(scan_mode); + OPT_COPY_IF_SET(row_address_type); + OPT_COPY_IF_SET(multiplexing); + OPT_COPY_IF_SET(disable_hardware_pulsing); + OPT_COPY_IF_SET(show_refresh_rate); + OPT_COPY_IF_SET(inverse_colors); + OPT_COPY_IF_SET(led_rgb_sequence); + OPT_COPY_IF_SET(pixel_mapper_config); + OPT_COPY_IF_SET(panel_type); + OPT_COPY_IF_SET(limit_refresh_rate_hz); + OPT_COPY_IF_SET(disable_busy_waiting); +#undef OPT_COPY_IF_SET + } + + if (rt_opts) { + // Same story as RGBMatrix::Options +#define RT_OPT_COPY_IF_SET(o) if (rt_opts->o) default_rt.o = rt_opts->o + RT_OPT_COPY_IF_SET(gpio_slowdown); + RT_OPT_COPY_IF_SET(rp1_pio); + RT_OPT_COPY_IF_SET(daemon); + RT_OPT_COPY_IF_SET(drop_privileges); + RT_OPT_COPY_IF_SET(do_gpio_init); + RT_OPT_COPY_IF_SET(drop_priv_user); + RT_OPT_COPY_IF_SET(drop_priv_group); +#undef RT_OPT_COPY_IF_SET + } + + rgb_matrix::RGBMatrix::Options matrix_options = default_opts; + rgb_matrix::RuntimeOptions runtime_opt = default_rt; + if (argc != NULL && argv != NULL) { + if (!ParseOptionsFromFlags(argc, argv, &matrix_options, &runtime_opt, + remove_consumed_flags)) { + rgb_matrix::PrintMatrixFlags(stderr, default_opts, default_rt); + return NULL; + } + } + + if (opts) { +#define ACTUAL_VALUE_BACK_TO_OPT(o) opts->o = matrix_options.o + ACTUAL_VALUE_BACK_TO_OPT(hardware_mapping); + ACTUAL_VALUE_BACK_TO_OPT(rows); + ACTUAL_VALUE_BACK_TO_OPT(cols); + ACTUAL_VALUE_BACK_TO_OPT(chain_length); + ACTUAL_VALUE_BACK_TO_OPT(parallel); + ACTUAL_VALUE_BACK_TO_OPT(pwm_bits); + ACTUAL_VALUE_BACK_TO_OPT(pwm_lsb_nanoseconds); + ACTUAL_VALUE_BACK_TO_OPT(pwm_dither_bits); + ACTUAL_VALUE_BACK_TO_OPT(brightness); + ACTUAL_VALUE_BACK_TO_OPT(scan_mode); + ACTUAL_VALUE_BACK_TO_OPT(row_address_type); + ACTUAL_VALUE_BACK_TO_OPT(multiplexing); + ACTUAL_VALUE_BACK_TO_OPT(disable_hardware_pulsing); + ACTUAL_VALUE_BACK_TO_OPT(show_refresh_rate); + ACTUAL_VALUE_BACK_TO_OPT(inverse_colors); + ACTUAL_VALUE_BACK_TO_OPT(led_rgb_sequence); + ACTUAL_VALUE_BACK_TO_OPT(pixel_mapper_config); + ACTUAL_VALUE_BACK_TO_OPT(panel_type); + ACTUAL_VALUE_BACK_TO_OPT(limit_refresh_rate_hz); + ACTUAL_VALUE_BACK_TO_OPT(disable_busy_waiting); +#undef ACTUAL_VALUE_BACK_TO_OPT + } + + if (rt_opts) { +#define ACTUAL_VALUE_BACK_TO_RT_OPT(o) rt_opts->o = runtime_opt.o + ACTUAL_VALUE_BACK_TO_RT_OPT(gpio_slowdown); + ACTUAL_VALUE_BACK_TO_RT_OPT(rp1_pio); + ACTUAL_VALUE_BACK_TO_RT_OPT(daemon); + ACTUAL_VALUE_BACK_TO_RT_OPT(drop_privileges); + ACTUAL_VALUE_BACK_TO_RT_OPT(do_gpio_init); + ACTUAL_VALUE_BACK_TO_RT_OPT(drop_priv_user); + ACTUAL_VALUE_BACK_TO_RT_OPT(drop_priv_group); +#undef ACTUAL_VALUE_BACK_TO_RT_OPT + } + + rgb_matrix::RGBMatrix *matrix + = rgb_matrix::RGBMatrix::CreateFromOptions(matrix_options, runtime_opt); + return from_matrix(matrix); +} + +struct RGBLedMatrix *led_matrix_create_from_options( + struct RGBLedMatrixOptions *opts, int *argc, char ***argv) { + return led_matrix_create_from_options_optional_edit(opts, NULL, argc, argv, + true); +} + +struct RGBLedMatrix *led_matrix_create_from_options_const_argv( + struct RGBLedMatrixOptions *opts, int argc, char **argv) { + return led_matrix_create_from_options_optional_edit(opts, NULL, &argc, &argv, + false); +} + +struct RGBLedMatrix *led_matrix_create_from_options_and_rt_options( + struct RGBLedMatrixOptions *opts, struct RGBLedRuntimeOptions * rt_opts) { + return led_matrix_create_from_options_optional_edit(opts, rt_opts, NULL, NULL, + false); +} + +struct RGBLedMatrix *led_matrix_create(int rows, int chained, int parallel) { + struct RGBLedMatrixOptions opts; + memset(&opts, 0, sizeof(opts)); + opts.rows = rows; + opts.chain_length = chained; + opts.parallel = parallel; + return led_matrix_create_from_options(&opts, NULL, NULL); +} + +void led_matrix_print_flags(FILE *out) { + rgb_matrix::RGBMatrix::Options defaults; + rgb_matrix::RuntimeOptions rt_opt; + rt_opt.daemon = -1; + rt_opt.drop_privileges = -1; + rgb_matrix::PrintMatrixFlags(out, defaults, rt_opt); +} + +void led_matrix_delete(struct RGBLedMatrix *matrix) { + delete to_matrix(matrix); +} + +struct LedCanvas *led_matrix_get_canvas(struct RGBLedMatrix *matrix) { + return from_canvas(to_matrix(matrix)->SwapOnVSync(NULL)); +} + +struct LedCanvas *led_matrix_create_offscreen_canvas(struct RGBLedMatrix *m) { + return from_canvas(to_matrix(m)->CreateFrameCanvas()); +} + +struct LedCanvas *led_matrix_swap_on_vsync(struct RGBLedMatrix *matrix, + struct LedCanvas *canvas) { + return from_canvas(to_matrix(matrix)->SwapOnVSync(to_canvas(canvas))); +} + +void led_matrix_set_brightness(struct RGBLedMatrix *matrix, + uint8_t brightness) { + to_matrix(matrix)->SetBrightness(brightness); +} + +uint8_t led_matrix_get_brightness(struct RGBLedMatrix *matrix) { + return to_matrix(matrix)->brightness(); +} + +void led_canvas_get_size(const struct LedCanvas *canvas, + int *width, int *height) { + rgb_matrix::FrameCanvas *c = to_canvas((struct LedCanvas*)canvas); + if (c == NULL ) return; + if (width != NULL) *width = c->width(); + if (height != NULL) *height = c->height(); +} + +void led_canvas_set_pixel(struct LedCanvas *canvas, int x, int y, + uint8_t r, uint8_t g, uint8_t b) { + to_canvas(canvas)->SetPixel(x, y, r, g, b); +} + +void led_canvas_set_pixels(struct LedCanvas *canvas, int x, int y, + int width, int height, struct Color *colors) { + to_canvas(canvas)->SetPixels(x, y, width, height, to_color(colors)); +} + +void led_canvas_clear(struct LedCanvas *canvas) { + to_canvas(canvas)->Clear(); +} + +void led_canvas_fill(struct LedCanvas *canvas, uint8_t r, uint8_t g, uint8_t b) { + to_canvas(canvas)->Fill(r, g, b); +} + +void led_canvas_subfill(struct LedCanvas *canvas, int x, int y, int width, int height, uint8_t r, uint8_t g, uint8_t b) { + to_canvas(canvas)->SubFill(x, y, width, height, r, g, b); +} +struct LedFont *load_font(const char *bdf_font_file) { + rgb_matrix::Font* font = new rgb_matrix::Font(); + font->LoadFont(bdf_font_file); + return from_font(font); +} + +int baseline_font(struct LedFont * font) { + return to_font(font)->baseline(); +} + +int height_font(struct LedFont * font) { + return to_font(font)->height(); +} + +int character_width_font(struct LedFont *font, uint32_t unicode_codepoint) { + return to_font(font)->CharacterWidth(unicode_codepoint); +} + +struct LedFont *create_outline_font(struct LedFont * font) { + rgb_matrix::Font* outlineFont = to_font(font)->CreateOutlineFont(); + return from_font(outlineFont); +} + +void delete_font(struct LedFont *font) { + delete to_font(font); +} + +// -- Some utility functions. + +void set_image(struct LedCanvas *c, int canvas_offset_x, int canvas_offset_y, + const uint8_t *image_buffer, size_t buffer_size_bytes, + int image_width, int image_height, + char is_bgr) { + SetImage(to_canvas(c), canvas_offset_x, canvas_offset_y, + image_buffer, buffer_size_bytes, + image_width, image_height, + is_bgr); +} + +// Draw text, a standard NUL terminated C-string encoded in UTF-8, +// with given "font" at "x","y" with "color". +// "color" always needs to be set (hence it is a reference), +// "background_color" is a pointer to optionally be NULL for transparency. +// "kerning_offset" allows for additional spacing between characters (can be +// negative) +// Returns how many pixels we advanced on the screen. +int draw_text(struct LedCanvas *c, struct LedFont *font, int x, int y, + uint8_t r, uint8_t g, uint8_t b, const char *utf8_text, int kerning_offset) { + const rgb_matrix::Color col = rgb_matrix::Color(r, g, b); + return DrawText(to_canvas(c), *to_font(font), x, y, col, NULL, utf8_text, kerning_offset); +} + +// Draw text, a standard NUL terminated C-string encoded in UTF-8, +// with given "font" at "x","y" with "color". +// Draw text as above, but vertically (top down). +// The text is a standard NUL terminated C-string encoded in UTF-8. +// "font, "x", "y", "color" and "background_color" are same as DrawText(). +// "kerning_offset" allows for additional spacing between characters (can be +// negative). +// Returns font height to advance up on the screen. +int vertical_draw_text(struct LedCanvas *c, struct LedFont *font, int x, int y, + uint8_t r, uint8_t g, uint8_t b, + const char *utf8_text, int kerning_offset = 0) { + const rgb_matrix::Color col = rgb_matrix::Color(r, g, b); + return VerticalDrawText(to_canvas(c), *to_font(font), x, y, col, NULL, utf8_text, kerning_offset); +} + +// Draw a circle centered at "x", "y", with a radius of "radius" and with "color" +void draw_circle(struct LedCanvas *c, int xx, int y, int radius, uint8_t r, uint8_t g, uint8_t b) { + const rgb_matrix::Color col = rgb_matrix::Color( r,g,b ); + DrawCircle(to_canvas(c), xx, y, radius, col); +} + +// Draw a line from "x0", "y0" to "x1", "y1" and with "color" +void draw_line(struct LedCanvas *c, int x0, int y0, int x1, int y1, uint8_t r, uint8_t g, uint8_t b) { + const rgb_matrix::Color col = rgb_matrix::Color(r, g, b); + DrawLine(to_canvas(c), x0, y0, x1, y1, col); +} diff --git a/deps/rpi-rgb-led-matrix/lib/led-matrix.cc b/deps/rpi-rgb-led-matrix/lib/led-matrix.cc new file mode 100644 index 000000000000..ab2f9651163d --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/led-matrix.cc @@ -0,0 +1,911 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see + +#include "led-matrix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gpio.h" +#include "rp1/rp1_pio_backend.h" +#include "rp1/rp1_rio_backend.h" +#include "thread.h" +#include "framebuffer-internal.h" +#include "multiplex-mappers-internal.h" + +// C wrapper to reset global GPIO bookkeeping from external callers. +extern "C" void ledmatrix_reset_global_gpio(); + +// Global GPIO instance used by the library. Previously this was a function- +// local static inside CreateFromOptions; move it to file-scope so the C +// wrapper can access and reset it. +static rgb_matrix::GPIO s_global_io; // file-scope global + +// Implement the C wrapper here so it has file scope and can access +// the file-scope GPIO instance. +extern "C" void ledmatrix_reset_global_gpio() { s_global_io.ResetState(); } + +// Leave this in here for a while. Setting things from old defines. +#if defined(ADAFRUIT_RGBMATRIX_HAT) +# error "ADAFRUIT_RGBMATRIX_HAT has long been deprecated. Please use the Options struct or --led-gpio-mapping=adafruit-hat commandline flag" +#endif + +#if defined(ADAFRUIT_RGBMATRIX_HAT_PWM) +# error "ADAFRUIT_RGBMATRIX_HAT_PWM has long been deprecated. Please use the Options struct or --led-gpio-mapping=adafruit-hat-pwm commandline flag" +#endif + +namespace rgb_matrix { +// Implementation details of RGBmatrix. +class RGBMatrix::Impl { + class UpdateThread; + friend class UpdateThread; + +public: + // Create an RGBMatrix. + // + // Needs an initialized GPIO object and configuration options from the + // RGBMatrix::Options struct. + // + // If you pass an GPIO object (which has to be Init()ialized), it will start // the internal thread to start the screen immediately. + // + // If you need finer control over when the refresh thread starts (which you + // might when you become a daemon), pass NULL here and see SetGPIO() method. + // + // The resulting canvas is (options.rows * options.parallel) high and + // (32 * options.chain_length) wide. + Impl(GPIO *io, const Options &options); + + ~Impl(); + + // Used to be there to help user delay initialization of thread starting, + // these days only used internally. + void SetGPIO(GPIO *io, bool start_thread = true); + + bool StartRefresh(); + + FrameCanvas *CreateFrameCanvas(); + FrameCanvas *SwapOnVSync(FrameCanvas *other, unsigned framerate_fraction); + bool ApplyPixelMapper(const PixelMapper *mapper); + + bool SetPWMBits(uint8_t value); + uint8_t pwmbits(); // return the pwm-bits of the currently active buffer. + + void set_luminance_correct(bool on); + bool luminance_correct() const; + + // Set brightness in percent for all created FrameCanvas. 1%..100%. + // This will only affect newly set pixels. + void SetBrightness(uint8_t brightness); + uint8_t brightness(); + + uint64_t RequestInputs(uint64_t); + uint64_t AwaitInputChange(int timeout_ms); + + uint64_t RequestOutputs(uint64_t output_bits); + void OutputGPIO(uint64_t output_bits); + +private: + friend class RGBMatrix; + + // Apply pixel mappers that have been passed down via a configuration + // string. + void ApplyNamedPixelMappers(const char *pixel_mapper_config, + int chain, int parallel); + + Options params_; + bool do_luminance_correct_; + + FrameCanvas *active_; + + GPIO *io_; + Mutex active_frame_sync_; + UpdateThread *updater_; + std::vector created_frames_; + internal::PixelDesignatorMap *shared_pixel_mapper_; + uint64_t user_output_bits_; +}; + +using namespace internal; + +// Pump pixels to screen. Needs to be high priority real-time because jitter +class RGBMatrix::Impl::UpdateThread : public Thread { +public: + UpdateThread(GPIO *io, FrameCanvas *initial_frame, + int pwm_dither_bits, bool show_refresh, + int limit_refresh_hz, bool allow_busy_waiting) + : io_(io), show_refresh_(show_refresh), + target_frame_usec_(limit_refresh_hz < 1 ? 0 : 1e6/limit_refresh_hz), + allow_busy_waiting_(allow_busy_waiting), + running_(true), + current_frame_(initial_frame), next_frame_(NULL), + requested_frame_multiple_(1) { + pthread_cond_init(&frame_done_, NULL); + pthread_cond_init(&input_change_, NULL); + switch (pwm_dither_bits) { + case 0: + start_bit_[0] = 0; start_bit_[1] = 0; + start_bit_[2] = 0; start_bit_[3] = 0; + break; + case 1: + start_bit_[0] = 0; start_bit_[1] = 1; + start_bit_[2] = 0; start_bit_[3] = 1; + break; + case 2: + start_bit_[0] = 0; start_bit_[1] = 1; + start_bit_[2] = 2; start_bit_[3] = 2; + break; + } + } + + void Stop() { + MutexLock l(&running_mutex_); + running_ = false; + } + + virtual void Run() { + unsigned frame_count = 0; + unsigned low_bit_sequence = 0; + uint32_t largest_time = 0; + gpio_bits_t last_gpio_bits = 0; + + // Let's start measure max time only after a we were running for a few + // seconds to not pick up start-up glitches. + static const int kHoldffTimeUs = 2000 * 1000; + uint32_t initial_holdoff_start = GetMicrosecondCounter(); + bool max_measure_enabled = false; + + while (running()) { + const uint32_t start_time_us = GetMicrosecondCounter(); + + current_frame_->framebuffer() + ->DumpToMatrix(io_, start_bit_[low_bit_sequence % 4]); + + // SwapOnVSync() exchange. + { + MutexLock l(&frame_sync_); + // Do fast equality test first (likely due to frame_count reset). + if (frame_count == requested_frame_multiple_ + || frame_count % requested_frame_multiple_ == 0) { + // We reset to avoid frame hick-up every couple of weeks + // run-time iff requested_frame_multiple_ is not a factor of 2^32. + frame_count = 0; + if (next_frame_ != NULL) { + current_frame_ = next_frame_; + next_frame_ = NULL; + } + pthread_cond_signal(&frame_done_); + } + } + + // Read input bits. + if (!Rp1PioIsActive() && !Rp1RioIsActive()) { + const gpio_bits_t inputs = io_->Read(); + if (inputs != last_gpio_bits) { + last_gpio_bits = inputs; + MutexLock l(&input_sync_); + gpio_inputs_ = inputs; + pthread_cond_signal(&input_change_); + } + } + + ++frame_count; + ++low_bit_sequence; + + if (target_frame_usec_) { + if (allow_busy_waiting_) { + while ((GetMicrosecondCounter() - start_time_us) < target_frame_usec_) { + // busy wait. We have our dedicated core, so ok to burn cycles. + } + } else { + long spent_us = GetMicrosecondCounter() - start_time_us; + SleepMicroseconds(target_frame_usec_ - spent_us); + } + } + + const uint32_t end_time_us = GetMicrosecondCounter(); + if (show_refresh_) { + uint32_t usec = end_time_us - start_time_us; + printf("\b\b\b\b\b\b\b\b%6.1fHz", 1e6 / usec); + if (usec > largest_time && max_measure_enabled) { + largest_time = usec; + const float lowest_hz = 1e6 / largest_time; + printf(" (lowest: %.1fHz)" + "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", lowest_hz); + } else { + // Don't measure at startup, as times will be janky. + max_measure_enabled = (end_time_us - initial_holdoff_start) > kHoldffTimeUs; + } + } + } + } + + FrameCanvas *SwapOnVSync(FrameCanvas *other, unsigned frame_fraction) { + MutexLock l(&frame_sync_); + FrameCanvas *previous = current_frame_; + next_frame_ = other; + requested_frame_multiple_ = frame_fraction; + frame_sync_.WaitOn(&frame_done_); + return previous; + } + + gpio_bits_t AwaitInputChange(int timeout_ms) { + MutexLock l(&input_sync_); + input_sync_.WaitOn(&input_change_, timeout_ms); + return gpio_inputs_; + } + +private: + inline bool running() { + MutexLock l(&running_mutex_); + return running_; + } + + GPIO *const io_; + const bool show_refresh_; + const uint32_t target_frame_usec_; + const bool allow_busy_waiting_; + uint32_t start_bit_[4]; + + Mutex running_mutex_; + bool running_; + + Mutex input_sync_; + pthread_cond_t input_change_; + gpio_bits_t gpio_inputs_; + + Mutex frame_sync_; + pthread_cond_t frame_done_; + FrameCanvas *current_frame_; + FrameCanvas *next_frame_; + unsigned requested_frame_multiple_; +}; + +// Some defaults. See options-initialize.cc for the command line parsing. +RGBMatrix::Options::Options() : + // Historically, we provided these options only as #defines. Make sure that + // things still behave as before if someone has set these. + // At some point: remove them from the Makefile. Later: remove them here. +#ifdef DEFAULT_HARDWARE + hardware_mapping(DEFAULT_HARDWARE), +#else + hardware_mapping("regular"), +#endif + + rows(32), cols(32), chain_length(1), parallel(1), + pwm_bits(internal::Framebuffer::kDefaultBitPlanes), + +#ifdef LSB_PWM_NANOSECONDS + pwm_lsb_nanoseconds(LSB_PWM_NANOSECONDS), +#else + pwm_lsb_nanoseconds(130), +#endif + + pwm_dither_bits(0), + brightness(100), + +#ifdef RGB_SCAN_INTERLACED + scan_mode(1), +#else + scan_mode(0), +#endif + + row_address_type(0), + multiplexing(0), + +#ifdef DISABLE_HARDWARE_PULSES + disable_hardware_pulsing(true), +#else + disable_hardware_pulsing(false), +#endif + +#ifdef SHOW_REFRESH_RATE + show_refresh_rate(true), +#else + show_refresh_rate(false), +#endif + +#ifdef INVERSE_RGB_DISPLAY_COLORS + inverse_colors(true), +#else + inverse_colors(false), +#endif + led_rgb_sequence("RGB"), + pixel_mapper_config(NULL), + panel_type(NULL), +#ifdef FIXED_FRAME_MICROSECONDS + limit_refresh_rate_hz(1e6 / FIXED_FRAME_MICROSECONDS), +#else + limit_refresh_rate_hz(0), +#endif +#ifdef DISABLE_BUSY_WAITING + disable_busy_waiting(true) +#else + disable_busy_waiting(false) +#endif +{ + // Nothing to see here. +} + +#define DEBUG_MATRIX_OPTIONS 0 + +#if DEBUG_MATRIX_OPTIONS +static void PrintOptions(const RGBMatrix::Options &o) { +#define P_INT(val) fprintf(stderr, "%s : %d\n", #val, o.val) +#define P_STR(val) fprintf(stderr, "%s : %s\n", #val, o.val) +#define P_BOOL(val) fprintf(stderr, "%s : %s\n", #val, o.val ? "true":"false") + P_STR(hardware_mapping); + P_INT(rows); + P_INT(cols); + P_INT(chain_length); + P_INT(parallel); + P_INT(pwm_bits); + P_INT(pwm_lsb_nanoseconds); + P_INT(pwm_dither_bits); + P_INT(brightness); + P_INT(scan_mode); + P_INT(row_address_type); + P_INT(multiplexing); + P_BOOL(disable_hardware_pulsing); + P_BOOL(show_refresh_rate); + P_BOOL(inverse_colors); + P_STR(led_rgb_sequence); + P_STR(pixel_mapper_config); + P_STR(panel_type); + P_INT(limit_refresh_rate_hz); + P_BOOL(disable_busy_waiting); +#undef P_INT +#undef P_STR +#undef P_BOOL +} +#endif // DEBUG_MATRIX_OPTIONS + +RGBMatrix::Impl::Impl(GPIO *io, const Options &options) + : params_(options), io_(NULL), updater_(NULL), shared_pixel_mapper_(NULL), + user_output_bits_(0) { + assert(params_.Validate(NULL)); +#if DEBUG_MATRIX_OPTIONS + PrintOptions(params_); +#endif + const MultiplexMapper *multiplex_mapper = NULL; + if (params_.multiplexing > 0) { + const MuxMapperList &multiplexers = GetRegisteredMultiplexMappers(); + if (params_.multiplexing <= (int) multiplexers.size()) { + // TODO: we could also do a find-by-name here, but not sure if worthwhile + multiplex_mapper = multiplexers[params_.multiplexing - 1]; + } + } + + if (multiplex_mapper) { + // The multiplexers might choose to have a different physical layout. + // We need to configure that first before setting up the hardware. + multiplex_mapper->EditColsRows(¶ms_.cols, ¶ms_.rows); + } + + Framebuffer::InitHardwareMapping(params_.hardware_mapping); + + active_ = CreateFrameCanvas(); + active_->Clear(); + SetGPIO(io, true); + + // We need to apply the mapping for the panels first. + ApplyPixelMapper(multiplex_mapper); + + // .. followed by higher level mappers that might arrange panels. + ApplyNamedPixelMappers(options.pixel_mapper_config, + params_.chain_length, params_.parallel); +} + +RGBMatrix::Impl::~Impl() { + if (updater_) { + updater_->Stop(); + updater_->WaitStopped(); + } + delete updater_; + + // Make sure LEDs are off. + active_->Clear(); + if (io_) active_->framebuffer()->DumpToMatrix(io_, 0); + internal::Rp1RioDeinit(); + internal::Rp1PioDeinit(); + + for (size_t i = 0; i < created_frames_.size(); ++i) { + delete created_frames_[i]; + } + delete shared_pixel_mapper_; +} + +RGBMatrix::~RGBMatrix() { + delete impl_; +} + +uint64_t RGBMatrix::Impl::RequestInputs(uint64_t bits) { + if (Rp1PioIsActive() || Rp1RioIsActive()) return 0; + return io_->RequestInputs(static_cast(bits)); +} + +uint64_t RGBMatrix::Impl::RequestOutputs(uint64_t output_bits) { + if (Rp1PioIsActive() || Rp1RioIsActive()) return 0; + uint64_t success_bits = io_->InitOutputs(static_cast(output_bits)); + user_output_bits_ |= success_bits; + return success_bits; +} + +void RGBMatrix::Impl::OutputGPIO(uint64_t output_bits) { + if (Rp1PioIsActive() || Rp1RioIsActive()) return; + io_->WriteMaskedBits(static_cast(output_bits), static_cast(user_output_bits_)); +} + +void RGBMatrix::Impl::ApplyNamedPixelMappers(const char *pixel_mapper_config, + int chain, int parallel) { + if (pixel_mapper_config == NULL || strlen(pixel_mapper_config) == 0) + return; + char *const writeable_copy = strdup(pixel_mapper_config); + const char *const end = writeable_copy + strlen(writeable_copy); + char *s = writeable_copy; + while (s < end) { + char *const semicolon = strchrnul(s, ';'); + *semicolon = '\0'; + char *optional_param_start = strchr(s, ':'); + if (optional_param_start) { + *optional_param_start++ = '\0'; + } + if (*s == '\0' && optional_param_start && *optional_param_start != '\0') { + fprintf(stderr, "Stray parameter ':%s' without mapper name ?\n", optional_param_start); + } + if (*s) { + ApplyPixelMapper(FindPixelMapper(s, chain, parallel, optional_param_start)); + } + s = semicolon + 1; + } + free(writeable_copy); +} + +void RGBMatrix::Impl::SetGPIO(GPIO *io, bool start_thread) { + if (io != NULL && io_ == NULL) { + io_ = io; + Framebuffer::InitGPIO(io_, params_.rows, params_.parallel, + !params_.disable_hardware_pulsing, + params_.pwm_lsb_nanoseconds, params_.pwm_dither_bits, + params_.row_address_type); + Framebuffer::InitializePanels(io_, params_.panel_type, + params_.cols * params_.chain_length); + } + if (start_thread) { + StartRefresh(); + } +} + +bool RGBMatrix::Impl::StartRefresh() { + if (updater_ == NULL && io_ != NULL) { + updater_ = new UpdateThread(io_, active_, params_.pwm_dither_bits, + params_.show_refresh_rate, + params_.limit_refresh_rate_hz, + !params_.disable_busy_waiting); + // If we have multiple processors, the kernel + // jumps around between these, creating some global flicker. + // So let's tie it to the last CPU available. + // The Raspberry Pi2 has 4 cores, our attempt to bind it to + // core #3 will succeed. + // The Raspberry Pi1 only has one core, so this affinity + // call will simply fail and we keep using the only core. + updater_->Start(99, (1<<3)); // Prio: high. Also: put on last CPU. + } + return updater_ != NULL; +} + +FrameCanvas *RGBMatrix::Impl::CreateFrameCanvas() { + FrameCanvas *result = + new FrameCanvas(new Framebuffer(params_.rows, + params_.cols * params_.chain_length, + params_.parallel, + params_.scan_mode, + params_.led_rgb_sequence, + params_.inverse_colors, + &shared_pixel_mapper_)); + if (created_frames_.empty()) { + // First time. Get defaults from initial Framebuffer. + do_luminance_correct_ = result->framebuffer()->luminance_correct(); + } + + result->framebuffer()->SetPWMBits(params_.pwm_bits); + result->framebuffer()->set_luminance_correct(do_luminance_correct_); + result->framebuffer()->SetBrightness(params_.brightness); + + created_frames_.push_back(result); + + if (created_frames_.size() % 500 == 0) { + if (created_frames_.size() == 500) { + fprintf(stderr, "CreateFrameCanvas() called %d times; Usually you only want to call it once (or at most a few times) for double-buffering. These frames will not be freed until the end of the program.\n" + "Typical reasons: \n" + " * Accidentally called CreateFrameCanvas() inside your inner loop (move outside the loop. Create offscreen-canvas once, then re-use. See SwapOnVSync() examples).\n" + " * Used to pre-compute many frames (use led_matrix::StreamWriter instead for such use-case. See e.g. led-image-viewer)\n", + (int)created_frames_.size()); + } else { + fprintf(stderr, "FYI: CreateFrameCanvas() now called %d times.\n", + (int)created_frames_.size()); + } + } + + return result; +} + +FrameCanvas *RGBMatrix::Impl::SwapOnVSync(FrameCanvas *other, + unsigned frame_fraction) { + if (frame_fraction == 0) frame_fraction = 1; // correct user error. + if (!updater_) return NULL; + FrameCanvas *const previous = updater_->SwapOnVSync(other, frame_fraction); + if (other) active_ = other; + return previous; +} + +uint64_t RGBMatrix::Impl::AwaitInputChange(int timeout_ms) { + if (!updater_) return 0; + if (Rp1PioIsActive() || Rp1RioIsActive()) return 0; + return updater_->AwaitInputChange(timeout_ms); +} + +bool RGBMatrix::Impl::SetPWMBits(uint8_t value) { + const bool success = active_->framebuffer()->SetPWMBits(value); + if (success) { + params_.pwm_bits = value; + } + return success; +} +uint8_t RGBMatrix::Impl::pwmbits() { return params_.pwm_bits; } + +// Map brightness of output linearly to input with CIE1931 profile. +void RGBMatrix::Impl::set_luminance_correct(bool on) { + active_->framebuffer()->set_luminance_correct(on); + do_luminance_correct_ = on; +} +bool RGBMatrix::Impl::luminance_correct() const { + return do_luminance_correct_; +} + +void RGBMatrix::Impl::SetBrightness(uint8_t brightness) { + for (size_t i = 0; i < created_frames_.size(); ++i) { + created_frames_[i]->framebuffer()->SetBrightness(brightness); + } + params_.brightness = brightness; +} + +uint8_t RGBMatrix::Impl::brightness() { + return params_.brightness; +} + +bool RGBMatrix::Impl::ApplyPixelMapper(const PixelMapper *mapper) { + if (mapper == NULL) return true; + using internal::PixelDesignatorMap; + const int old_width = shared_pixel_mapper_->width(); + const int old_height = shared_pixel_mapper_->height(); + int new_width, new_height; + if (!mapper->GetSizeMapping(old_width, old_height, &new_width, &new_height)) { + return false; + } + PixelDesignatorMap *new_mapper = new PixelDesignatorMap( + new_width, new_height, shared_pixel_mapper_->GetFillColorBits()); + switch (mapper->GetMappingType()) { + case PixelMapper::VisibleToMatrix: + for (int y = 0; y < new_height; ++y) { + for (int x = 0; x < new_width; ++x) { + int orig_x = -1, orig_y = -1; + mapper->MapVisibleToMatrix(old_width, old_height, + x, y, &orig_x, &orig_y); + if (orig_x < 0 || orig_y < 0 || + orig_x >= old_width || orig_y >= old_height) { + fprintf(stderr, "Error in PixelMapper: (%d, %d) -> (%d, %d) [range: " + "%dx%d]\n", x, y, orig_x, orig_y, old_width, old_height); + continue; + } + const internal::PixelDesignator *orig_designator; + orig_designator = shared_pixel_mapper_->get(orig_x, orig_y); + *new_mapper->get(x, y) = *orig_designator; + } + } + break; + case PixelMapper::MatrixToVisible: { + bool collision_reported = false; + for (int y = 0; y < old_height; ++y) { + for (int x = 0; x < old_width; ++x) { + int new_x = -1, new_y = -1; + if (mapper->MapMatrixToVisible(old_width, old_height, + x, y, &new_x, &new_y)) { + if (new_x < 0 || new_y < 0 || + new_x >= new_width || new_y >= new_height) { + fprintf(stderr, "Error in PixelMapper MapMatrixToVisible: (%d, %d) " + "-> (%d, %d) [range: %dx%d]\n", + x, y, new_x, new_y, new_width, new_height); + continue; + } + const internal::PixelDesignator *orig_designator; + orig_designator = shared_pixel_mapper_->get(x, y); + internal::PixelDesignator *new_designator = new_mapper->get(new_x, new_y); + if (new_designator->gpio_word >= 0 && !collision_reported) { + fprintf(stderr, "Warning: MapMatrixToVisible: %s mapped twice to the same pixel (%d, %d) -> (%d, %d)\n", mapper->GetName(), x, y, new_x, new_y); + collision_reported = true; + } + *new_designator = *orig_designator; + } + } + } + break; + } + } + delete shared_pixel_mapper_; + shared_pixel_mapper_ = new_mapper; + return true; +} + +// -- Public interface of RGBMatrix. Delegate everything to impl_ + +static bool drop_privs(const char *priv_user, const char *priv_group) { + uid_t ruid, euid, suid; + if (getresuid(&ruid, &euid, &suid) >= 0) { + if (euid != 0) // not root anyway. No priv dropping. + return true; + } + + if (priv_user == nullptr || priv_user[0] == 0) priv_user = "daemon"; + if (priv_group == nullptr || priv_group[0] == 0) priv_group = "daemon"; + + gid_t gid = atoi(priv_group); // Attempt to parse as GID first + if (gid == 0) { + struct group *g = getgrnam(priv_group); + if (g == NULL) { + perror("group lookup."); + return false; + } + gid = g->gr_gid; + } + if (setresgid(gid, gid, gid) != 0) { + perror("setresgid()"); + return false; + } + + uid_t uid = atoi(priv_user); // Attempt to parse as UID first. + if (uid == 0) { + struct passwd *p = getpwnam(priv_user); + if (p == NULL) { + perror("user lookup."); + return false; + } + uid = p->pw_uid; + } + if (setresuid(uid, uid, uid) != 0) { + perror("setresuid()"); + return false; + } + return true; +} + +RGBMatrix *RGBMatrix::CreateFromOptions(const RGBMatrix::Options &options, + const RuntimeOptions &runtime_options) { + std::string error; + if (!options.Validate(&error)) { + fprintf(stderr, "%s\n", error.c_str()); + return NULL; + } + + // For the Pi4, we might need 2, maybe up to 4. Let's open up to 5. + // on supported architectures, -1 will emit memory barier (DSB ST) after GPIO write + if (runtime_options.gpio_slowdown < (LED_MATRIX_ALLOW_BARRIER_DELAY ? -1 : 0) + || runtime_options.gpio_slowdown > 60) { + fprintf(stderr, "--led-slowdown-gpio=%d is outside usable range\n", + runtime_options.gpio_slowdown); + return NULL; + } + if (runtime_options.rp1_pio != 0 && runtime_options.rp1_pio != 1) { + fprintf(stderr, "--led-rp1-pio=%d is outside usable range 0..1\n", + runtime_options.rp1_pio); + return NULL; + } + + // Use file-scope GPIO instance. + GPIO &io = s_global_io; + Rp1RioSetEnabled(runtime_options.rp1_pio == 0); + const bool use_rp1_rio = runtime_options.do_gpio_init + && Rp1RioShouldActivate(options.hardware_mapping, + options.row_address_type, + options.parallel); + const bool use_rp1_pio = !use_rp1_rio && runtime_options.do_gpio_init + && Rp1PioShouldActivate(options.hardware_mapping, + options.row_address_type, + options.parallel); + if (use_rp1_rio) { + Rp1RioSetGpioSlowdown(runtime_options.gpio_slowdown); + } + if (use_rp1_pio) { + Rp1PioSetGpioSlowdown(runtime_options.gpio_slowdown); + } + + const bool pi5_backend_available = + Rp1PioPlatformDetected() || Rp1RioPlatformDetected(); + if (runtime_options.do_gpio_init && pi5_backend_available + && !use_rp1_pio && !use_rp1_rio) { + if (Rp1RioBackendRequested()) { + fprintf(stderr, + "Pi 5-family RP1 RIO backend is selected, but this " + "configuration is not supported yet.\n" + "RIO supports mappings " + "regular/adafruit-hat/adafruit-hat-pwm/classic and " + "--led-row-addr-type=0, 1, 2, 3, 4, or 5.\n" + "For configurations supported by PIO, use --led-rp1-pio=1.\n"); + } else { + fprintf(stderr, + "Pi 5-family RP1 PIO backend is selected, but this " + "configuration is not supported yet.\n" + "Supported in PIO mode for now: mappings " + "regular/regular-pi1/adafruit-hat/adafruit-hat-pwm/classic and " + "--led-row-addr-type=0, 1, 2, 3, 4, or 5.\n"); + } + return NULL; + } + + // C wrapper implementation is at file scope; use local reference `io`. + if (runtime_options.do_gpio_init && !use_rp1_pio && !use_rp1_rio + && !io.Init(runtime_options.gpio_slowdown)) { + fprintf(stderr, "Must run as root to be able to access /dev/mem\n" + "Prepend 'sudo' to the command\n"); + return NULL; + } + + if (runtime_options.daemon > 0 && daemon(1, 0) != 0) { + perror("Failed to become daemon"); + } + + RGBMatrix::Impl *result = new RGBMatrix::Impl(NULL, options); + // Allowing daemon also means we are allowed to start the thread now. + const bool allow_daemon = !(runtime_options.daemon < 0); + if (runtime_options.do_gpio_init) + result->SetGPIO(&io, allow_daemon); + + // TODO(hzeller): if we disallow daemon, then we might also disallow + // drop privileges: we can't drop privileges until we have created the + // realtime thread that usually requires root to be established. + // Double check and document. + if (runtime_options.drop_privileges > 0) { + drop_privs(runtime_options.drop_priv_user, + runtime_options.drop_priv_group); + } + + return new RGBMatrix(result); +} + +// Public interface. +RGBMatrix *RGBMatrix::CreateFromFlags(int *argc, char ***argv, + RGBMatrix::Options *m_opt_in, + RuntimeOptions *rt_opt_in, + bool remove_consumed_options) { + RGBMatrix::Options scratch_matrix; + RGBMatrix::Options *mopt = (m_opt_in != NULL) ? m_opt_in : &scratch_matrix; + + RuntimeOptions scratch_rt; + RuntimeOptions *ropt = (rt_opt_in != NULL) ? rt_opt_in : &scratch_rt; + + if (!ParseOptionsFromFlags(argc, argv, mopt, ropt, remove_consumed_options)) + return NULL; + return CreateFromOptions(*mopt, *ropt); +} + +FrameCanvas *RGBMatrix::CreateFrameCanvas() { + return impl_->CreateFrameCanvas(); +} +FrameCanvas *RGBMatrix::SwapOnVSync(FrameCanvas *other, + unsigned framerate_fraction) { + return impl_->SwapOnVSync(other, framerate_fraction); +} +bool RGBMatrix::ApplyPixelMapper(const PixelMapper *mapper) { + return impl_->ApplyPixelMapper(mapper); +} +bool RGBMatrix::SetPWMBits(uint8_t value) { return impl_->SetPWMBits(value); } +uint8_t RGBMatrix::pwmbits() { return impl_->pwmbits(); } + +void RGBMatrix::set_luminance_correct(bool on) { + return impl_->set_luminance_correct(on); +} +bool RGBMatrix::luminance_correct() const { return impl_->luminance_correct(); } + +void RGBMatrix::SetBrightness(uint8_t brightness) { + impl_->SetBrightness(brightness); +} +uint8_t RGBMatrix::brightness() { return impl_->brightness(); } + +uint64_t RGBMatrix::RequestInputs(uint64_t all_interested_bits) { + return impl_->RequestInputs(all_interested_bits); +} +uint64_t RGBMatrix::AwaitInputChange(int timeout_ms) { + return impl_->AwaitInputChange(timeout_ms); +} + +uint64_t RGBMatrix::RequestOutputs(uint64_t all_interested_bits) { + return impl_->RequestOutputs(all_interested_bits); +} +void RGBMatrix::OutputGPIO(uint64_t output_bits) { + impl_->OutputGPIO(output_bits); +} + +bool RGBMatrix::StartRefresh() { return impl_->StartRefresh(); } + +// -- Implementation of RGBMatrix Canvas: delegation to ContentBuffer +int RGBMatrix::width() const { + return impl_->active_->width(); +} + +int RGBMatrix::height() const { + return impl_->active_->height(); +} + +void RGBMatrix::SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue) { + impl_->active_->SetPixel(x, y, red, green, blue); +} + +void RGBMatrix::Clear() { + impl_->active_->Clear(); +} + +void RGBMatrix::Fill(uint8_t red, uint8_t green, uint8_t blue) { + impl_->active_->Fill(red, green, blue); +} + +// FrameCanvas implementation of Canvas +FrameCanvas::~FrameCanvas() { delete frame_; } +int FrameCanvas::width() const { return frame_->width(); } +int FrameCanvas::height() const { return frame_->height(); } +void FrameCanvas::SetPixel(int x, int y, + uint8_t red, uint8_t green, uint8_t blue) { + frame_->SetPixel(x, y, red, green, blue); +} +void FrameCanvas::SetPixels(int x, int y, int width, int height, + Color *colors) { + frame_->SetPixels(x, y, width, height, colors); +} +void FrameCanvas::Clear() { return frame_->Clear(); } +void FrameCanvas::Fill(uint8_t red, uint8_t green, uint8_t blue) { + frame_->Fill(red, green, blue); +} +void FrameCanvas::SubFill(int x, int y, int width, int height, uint8_t red, uint8_t green, uint8_t blue) { + frame_->SubFill(x, y, width, height, red, green, blue); +} +bool FrameCanvas::SetPWMBits(uint8_t value) { return frame_->SetPWMBits(value); } +uint8_t FrameCanvas::pwmbits() { return frame_->pwmbits(); } + +// Map brightness of output linearly to input with CIE1931 profile. +void FrameCanvas::set_luminance_correct(bool on) { frame_->set_luminance_correct(on); } +bool FrameCanvas::luminance_correct() const { return frame_->luminance_correct(); } + +void FrameCanvas::SetBrightness(uint8_t brightness) { frame_->SetBrightness(brightness); } +uint8_t FrameCanvas::brightness() { return frame_->brightness(); } + +void FrameCanvas::Serialize(const char **data, size_t *len) const { + frame_->Serialize(data, len); +} +bool FrameCanvas::Deserialize(const char *data, size_t len) { + return frame_->Deserialize(data, len); +} +void FrameCanvas::CopyFrom(const FrameCanvas &other) { + frame_->CopyFrom(other.frame_); +} +} // end namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/multiplex-mappers-internal.h b/deps/rpi-rgb-led-matrix/lib/multiplex-mappers-internal.h new file mode 100644 index 000000000000..2addce5e7150 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/multiplex-mappers-internal.h @@ -0,0 +1,38 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2017 Henner Zeller +// +// 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. +// +// 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, see + +#include + +#include "pixel-mapper.h" + +namespace rgb_matrix { +namespace internal { + +class MultiplexMapper : public PixelMapper { +public: + // Function that edits the original rows and columns of the panels + // provided by the user to the actual underlying mapping. This is called + // before we do the actual set-up of the GPIO mapping as this influences + // the hardware interface. + // This is so that the user can provide the rows/columns they see. + virtual void EditColsRows(int *cols, int *rows) const = 0; +}; + +// Returns a vector of the registered Multiplex mappers. +typedef std::vector MuxMapperList; +const MuxMapperList &GetRegisteredMultiplexMappers(); + +} // namespace internal +} // namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/multiplex-mappers.cc b/deps/rpi-rgb-led-matrix/lib/multiplex-mappers.cc new file mode 100644 index 000000000000..0cebf1087f7b --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/multiplex-mappers.cc @@ -0,0 +1,591 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2017 Henner Zeller +// +// 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. +// +// 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, see + +#include "multiplex-mappers-internal.h" + +namespace rgb_matrix { +namespace internal { +// A Pixel Mapper maps physical pixels locations to the internal logical +// mapping in a panel or panel-assembly, which depends on the wiring. +class MultiplexMapperBase : public MultiplexMapper { +public: + MultiplexMapperBase(const char *name, int stretch_factor) + : name_(name), panel_stretch_factor_(stretch_factor) {} + + // This method is const, but we sneakily remember the original size + // of the panels so that we can more easily quantize things. + // So technically, we're stateful, but let's pretend we're not changing + // state. In the context this is used, it is never accessed in multiple + // threads. + virtual void EditColsRows(int *cols, int *rows) const { + panel_rows_ = *rows; + panel_cols_ = *cols; + + *rows /= panel_stretch_factor_; + *cols *= panel_stretch_factor_; + } + + virtual bool GetSizeMapping(int matrix_width, int matrix_height, + int *visible_width, int *visible_height) const { + // Matrix width has been altered. Alter it back. + *visible_width = matrix_width / panel_stretch_factor_; + *visible_height = matrix_height * panel_stretch_factor_; + return true; + } + + virtual const char *GetName() const { return name_; } + + // The MapVisibleToMatrix() as required by PanelMatrix here breaks it + // down to the individual panel, so that derived classes only need to + // implement MapSinglePanel(). + virtual void MapVisibleToMatrix(int matrix_width, int matrix_height, + int visible_x, int visible_y, + int *matrix_x, int *matrix_y) const { + const int chained_panel = visible_x / panel_cols_; + const int parallel_panel = visible_y / panel_rows_; + + const int within_panel_x = visible_x % panel_cols_; + const int within_panel_y = visible_y % panel_rows_; + + int new_x, new_y; + MapSinglePanel(within_panel_x, within_panel_y, &new_x, &new_y); + *matrix_x = chained_panel * panel_stretch_factor_*panel_cols_ + new_x; + *matrix_y = parallel_panel * panel_rows_/panel_stretch_factor_ + new_y; + } + + // Map the coordinates for a single panel. This is to be overridden in + // derived classes. + // Input parameter is the visible position on the matrix, and this method + // should return the internal multiplexed position. + virtual void MapSinglePanel(int visible_x, int visible_y, + int *matrix_x, int *matrix_y) const = 0; + +protected: + const char *const name_; + const int panel_stretch_factor_; + + mutable int panel_cols_; + mutable int panel_rows_; +}; + + +/* ======================================================================== + * Multiplexer implementations. + * + * Extend MultiplexMapperBase and implement MapSinglePanel. You only have + * to worry about the mapping within a single panel, the overall panel + * construction with chains and parallel is already taken care of. + * + * Don't forget to register the new multiplexer sin CreateMultiplexMapperList() + * below. After that, the new mapper is available in the --led-multiplexing + * option. + */ +class StripeMultiplexMapper : public MultiplexMapperBase { +public: + StripeMultiplexMapper() : MultiplexMapperBase("Stripe", 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const bool is_top_stripe = (y % (panel_rows_/2)) < panel_rows_/4; + *matrix_x = is_top_stripe ? x + panel_cols_ : x; + *matrix_y = ((y / (panel_rows_/2)) * (panel_rows_/4) + + y % (panel_rows_/4)); + } +}; + +class FlippedStripeMultiplexMapper : public MultiplexMapperBase { +public: + FlippedStripeMultiplexMapper() : MultiplexMapperBase("FlippedStripe", 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const bool is_top_stripe = (y % (panel_rows_/2)) >= panel_rows_/4; + *matrix_x = is_top_stripe ? x + panel_cols_ : x; + *matrix_y = ((y / (panel_rows_/2)) * (panel_rows_/4) + + y % (panel_rows_/4)); + } +}; + +class CheckeredMultiplexMapper : public MultiplexMapperBase { +public: + CheckeredMultiplexMapper() : MultiplexMapperBase("Checkered", 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const bool is_top_check = (y % (panel_rows_/2)) < panel_rows_/4; + const bool is_left_check = (x < panel_cols_/2); + if (is_top_check) { + *matrix_x = is_left_check ? x+panel_cols_/2 : x+panel_cols_; + } else { + *matrix_x = is_left_check ? x : x + panel_cols_/2; + } + *matrix_y = ((y / (panel_rows_/2)) * (panel_rows_/4) + + y % (panel_rows_/4)); + } +}; + +class SpiralMultiplexMapper : public MultiplexMapperBase { +public: + SpiralMultiplexMapper() : MultiplexMapperBase("Spiral", 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const bool is_top_stripe = (y % (panel_rows_/2)) < panel_rows_/4; + const int panel_quarter = panel_cols_/4; + const int quarter = x / panel_quarter; + const int offset = x % panel_quarter; + *matrix_x = ((2*quarter*panel_quarter) + + (is_top_stripe + ? panel_quarter - 1 - offset + : panel_quarter + offset)); + *matrix_y = ((y / (panel_rows_/2)) * (panel_rows_/4) + + y % (panel_rows_/4)); + } +}; + +class ZStripeMultiplexMapper : public MultiplexMapperBase { +public: + ZStripeMultiplexMapper(const char *name, int even_vblock_offset, int odd_vblock_offset) + : MultiplexMapperBase(name, 2), + even_vblock_offset_(even_vblock_offset), + odd_vblock_offset_(odd_vblock_offset) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + static const int tile_width = 8; + static const int tile_height = 4; + + const int vert_block_is_odd = ((y / tile_height) % 2); + + const int even_vblock_shift = (1 - vert_block_is_odd) * even_vblock_offset_; + const int odd_vblock_shitf = vert_block_is_odd * odd_vblock_offset_; + + *matrix_x = x + ((x + even_vblock_shift) / tile_width) * tile_width + odd_vblock_shitf; + *matrix_y = (y % tile_height) + tile_height * (y / (tile_height * 2)); + } + +private: + const int even_vblock_offset_; + const int odd_vblock_offset_; +}; + +class CoremanMapper : public MultiplexMapperBase { +public: + CoremanMapper() : MultiplexMapperBase("coreman", 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const bool is_left_check = (x < panel_cols_/2); + + if ((y <= 7) || ((y >= 16) && (y <= 23))){ + *matrix_x = ((x / (panel_cols_/2)) * panel_cols_) + (x % (panel_cols_/2)); + if ((y & (panel_rows_/4)) == 0) { + *matrix_y = (y / (panel_rows_/2)) * (panel_rows_/4) + (y % (panel_rows_/4)); + } + } else { + *matrix_x = is_left_check ? x + panel_cols_/2 : x + panel_cols_; + *matrix_y = (y / (panel_rows_/2)) * (panel_rows_/4) + y % (panel_rows_/4); + } + } +}; + +class Kaler2ScanMapper : public MultiplexMapperBase { +public: + Kaler2ScanMapper() : MultiplexMapperBase("Kaler2Scan", 4) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + // Now we have a 128x4 matrix + int offset = ((y%4)/2) == 0 ? -1 : 1;// Add or substract + int deltaOffset = offset < 0 ? 7:8; + int deltaColumn = ((y%8)/4)== 0 ? 64 : 0; + + *matrix_y = (y%2+(y/8)*2); + *matrix_x = deltaColumn + (16 * (x/8)) + deltaOffset + ((x%8) * offset); + + } +}; + +class P10MapperZ : public MultiplexMapperBase { +public: + P10MapperZ() : MultiplexMapperBase("P10-128x4-Z", 4) {} + // supports this panel: https://www.aliexpress.com/item/2017-Special-Offer-P10-Outdoor-Smd-Full-Color-Led-Display-Module-320x160mm-1-2-Scan-Outdoor/32809267439.html?spm=a2g0s.9042311.0.0.Ob0jEw + // with --led-row-addr-type=2 flag + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + int yComp = 0; + if (y == 0 || y == 1 || y == 8 || y == 9) { + yComp = 127; + } + else if (y == 2 || y == 3 || y == 10 || y == 11) { + yComp = 112; + } + else if (y == 4 || y == 5 || y == 12 || y == 13) { + yComp = 111; + } + else if (y == 6 || y == 7 || y == 14 || y == 15) { + yComp = 96; + } + + if (y == 0 || y == 1 || y == 4 || y == 5 || + y == 8 || y == 9 || y == 12 || y == 13) { + *matrix_x = yComp - x; + *matrix_x -= (24 * ((int)(x / 8))); + } + else { + *matrix_x = yComp + x; + *matrix_x -= (40 * ((int)(x / 8))); + } + + if (y == 0 || y == 2 || y == 4 || y == 6) { + *matrix_y = 3; + } + else if (y == 1 || y == 3 || y == 5 || y == 7) { + *matrix_y = 2; + } + else if (y == 8 || y == 10 || y == 12 || y == 14) { + *matrix_y = 1; + } + else if (y == 9 || y == 11 || y == 13 || y == 15) { + *matrix_y = 0; + } + } +}; + +class QiangLiQ8 : public MultiplexMapperBase { +public: + QiangLiQ8() : MultiplexMapperBase("QiangLiQ8", 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const int column = x + (4+ 4*(x/4)); + *matrix_x = column; + if ((y >= 15 && y <=19) || (y >= 5 && y <= 9)) { + const int reverseColumn = x + (4*(x/4)); + *matrix_x = reverseColumn; + } + *matrix_y = y % 5 + (y/10) *5; + } +}; + +class InversedZStripe : public MultiplexMapperBase { +public: + InversedZStripe() : MultiplexMapperBase("InversedZStripe", 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + static const int tile_width = 8; + static const int tile_height = 4; + + const int vert_block_is_odd = ((y / tile_height) % 2); + const int evenOffset[8] = {7, 5, 3, 1, -1, -3, -5, -7}; + + if (vert_block_is_odd) { + *matrix_x = x + (x / tile_width) * tile_width; + } else { + *matrix_x = x + (x / tile_width) * tile_width + 8 + evenOffset[x % 8]; + } + *matrix_y = (y % tile_height) + tile_height * (y / (tile_height * 2)); + } +}; + + +/* + * Various P10 1R1G1B Outdoor implementations for 16x16 modules with separate + * RGB LEDs, e.g.: + * https://www.ledcontrollercard.com/english/p10-outdoor-rgb-led-module-160x160mm-dip.html + * + */ +class P10Outdoor1R1G1BMultiplexBase : public MultiplexMapperBase { +public: + P10Outdoor1R1G1BMultiplexBase(const char *name) + : MultiplexMapperBase(name, 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const int vblock_is_odd = (y / tile_height_) % 2; + const int vblock_is_even = 1 - vblock_is_odd; + const int even_vblock_shift = vblock_is_even * even_vblock_offset_; + const int odd_vblock_shift = vblock_is_odd * odd_vblock_offset_; + + MapPanel(x, y, matrix_x, matrix_y, + vblock_is_even, vblock_is_odd, + even_vblock_shift, odd_vblock_shift); + } + +protected: + virtual void MapPanel(int x, int y, int *matrix_x, int *matrix_y, + int vblock_is_even, int vblock_is_odd, + int even_vblock_shift, int odd_vblock_shift) const = 0; + + static const int tile_width_ = 8; + static const int tile_height_ = 4; + static const int even_vblock_offset_ = 0; + static const int odd_vblock_offset_ = 8; +}; + +class P10Outdoor1R1G1BMultiplexMapper1 : public P10Outdoor1R1G1BMultiplexBase { +public: + P10Outdoor1R1G1BMultiplexMapper1() + : P10Outdoor1R1G1BMultiplexBase("P10Outdoor1R1G1-1") {} + +protected: + void MapPanel(int x, int y, int *matrix_x, int *matrix_y, + const int vblock_is_even, const int vblock_is_odd, + const int even_vblock_shift, const int odd_vblock_shift) const { + *matrix_x = tile_width_ * (1 + vblock_is_even + 2 * (x / tile_width_)) + - (x % tile_width_) - 1; + *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2)); + } +}; + +class P10Outdoor1R1G1BMultiplexMapper2 : public P10Outdoor1R1G1BMultiplexBase { +public: + P10Outdoor1R1G1BMultiplexMapper2() + : P10Outdoor1R1G1BMultiplexBase("P10Outdoor1R1G1-2") {} + +protected: + void MapPanel(int x, int y, int *matrix_x, int *matrix_y, + const int vblock_is_even, const int vblock_is_odd, + const int even_vblock_shift, const int odd_vblock_shift) const { + *matrix_x = vblock_is_even + ? tile_width_ * (1 + 2 * (x / tile_width_)) - (x % tile_width_) - 1 + : x + ((x + even_vblock_shift) / tile_width_) * tile_width_ + odd_vblock_shift; + *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2)); + } +}; + +class P10Outdoor1R1G1BMultiplexMapper3 : public P10Outdoor1R1G1BMultiplexBase { +public: + P10Outdoor1R1G1BMultiplexMapper3() + : P10Outdoor1R1G1BMultiplexBase("P10Outdoor1R1G1-3") {} + +protected: + void MapPanel(int x, int y, int *matrix_x, int *matrix_y, + const int vblock_is_even, const int vblock_is_odd, + const int even_vblock_shift, const int odd_vblock_shift) const { + *matrix_x = vblock_is_odd + ? tile_width_ * (2 + 2 * (x / tile_width_)) - (x % tile_width_) - 1 + : x + ((x + even_vblock_shift) / tile_width_) * tile_width_ + odd_vblock_shift; + *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2)); + } +}; + +class P10CoremanMapper : public MultiplexMapperBase { +public: + P10CoremanMapper() : MultiplexMapperBase("P10CoremanMapper", 4) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + //Row offset 8,8,8,8,0,0,0,0,8,8,8,8,0,0,0,0 + int mulY = (y & 4) > 0 ? 0 : 8; + + //Row offset 9,9,8,8,1,1,0,0,9,9,8,8,1,1,0,0 + mulY += (y & 2) > 0 ? 0 : 1; + mulY += (x >> 2) & ~1; //Drop lsb + + *matrix_x = (mulY << 3) + x % 8; + *matrix_y = (y & 1) + ((y >> 2) & ~1); + } +}; + +/* + * P8 1R1G1B Outdoor P8-5S-V3.2-HX 20x40 + */ +class P8Outdoor1R1G1BMultiplexBase : public MultiplexMapperBase { +public: + P8Outdoor1R1G1BMultiplexBase(const char *name) + : MultiplexMapperBase(name, 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const int vblock_is_odd = (y / tile_height_) % 2; + const int vblock_is_even = 1 - vblock_is_odd; + const int even_vblock_shift = vblock_is_even * even_vblock_offset_; + const int odd_vblock_shift = vblock_is_odd * odd_vblock_offset_; + + MapPanel(x, y, matrix_x, matrix_y, + vblock_is_even, vblock_is_odd, + even_vblock_shift, odd_vblock_shift); + } + +protected: + virtual void MapPanel(int x, int y, int *matrix_x, int *matrix_y, + int vblock_is_even, int vblock_is_odd, + int even_vblock_shift, int odd_vblock_shift) const = 0; + + static const int tile_width_ = 8; + static const int tile_height_ = 5; + static const int even_vblock_offset_ = 0; + static const int odd_vblock_offset_ = 8; +}; + +class P8Outdoor1R1G1BMultiplexMapper : public P8Outdoor1R1G1BMultiplexBase { +public: + P8Outdoor1R1G1BMultiplexMapper() + : P8Outdoor1R1G1BMultiplexBase("P8Outdoor1R1G1") {} + +protected: + void MapPanel(int x, int y, int *matrix_x, int *matrix_y, + const int vblock_is_even, const int vblock_is_odd, + const int even_vblock_shift, const int odd_vblock_shift) const { + + + *matrix_x = vblock_is_even + ? tile_width_ * (1 + tile_width_ - 2 * (x / tile_width_)) + tile_width_ - (x % tile_width_) - 1 + : tile_width_ * (1 + tile_width_ - 2 * (x / tile_width_)) - tile_width_ + (x % tile_width_); + + *matrix_y = (tile_height_ - y % tile_height_) + tile_height_ * (1 - y / (tile_height_ * 2)) -1; + + } +}; + +class P10Outdoor32x16HalfScanMapper : public MultiplexMapperBase { +public: + P10Outdoor32x16HalfScanMapper() : MultiplexMapperBase("P10Outdoor32x16HalfScan", 4) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + int base = (x/8)*32; + bool reverse = (y%4)/2 == 0; + int offset = (3-((y%8)/2))*8; + int dx = x%8; + + *matrix_y = (y/8 == 0) ? ((y%2 == 0) ? 0:1) : ((y%2 == 0) ? 2:3); + *matrix_x = base + (reverse ? offset + (7-dx) : offset + dx); + } +}; + +class P10Outdoor32x16QuarterScanMapper : public MultiplexMapperBase { +public: + P10Outdoor32x16QuarterScanMapper() : MultiplexMapperBase("P10Outdoor32x16QuarterScanMapper", 4) {} + // P10 quarter scan panel, e.g. https://www.ebay.com.au/itm/175517677191 + + void EditColsRows(int *cols, int *rows) const { + panel_rows_ = *rows; + panel_cols_ = *cols; + + *rows /= panel_stretch_factor_/2; // has half stretch factor in y compared to x + *cols *= panel_stretch_factor_; + } + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + int cell_starting_point = (x/8)*32; + int delta_x = x%8; + int offset = (3 - (y/4))*8; + *matrix_x = cell_starting_point + delta_x + offset; + *matrix_y = y%4; + } +}; + + +class P3Outdoor64x64MultiplexMapper : public MultiplexMapperBase { +public: + P3Outdoor64x64MultiplexMapper() : MultiplexMapperBase("P3Outdoor64x64MultiplexMapper", 2) {} + // P3 RGB panel 64x64 + // with pattern [1] [3] + // | \ | + // [0] [2] + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const bool is_top_stripe = (y % (panel_rows_/2)) < panel_rows_/4; + *matrix_x = ((x*2) + (is_top_stripe ? 1 : 0)); + *matrix_y = ((y / (panel_rows_/2)) * (panel_rows_/4) + + y % (panel_rows_/4)); + } +}; + +class DoubleZMultiplexMapper : public MultiplexMapperBase { + public: + DoubleZMultiplexMapper() : MultiplexMapperBase("DoubleZ", 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + const int quarter_rows = panel_rows_ / 4; + const int quarter_cols = panel_cols_ / 4; + + const int y_quarter = y / quarter_rows; // 0..3 + const int x_quarter = x / quarter_cols; // 0..3 + + const int offset_y = y % quarter_rows; + int offset_x = x % quarter_cols; + + const bool flip_quarter = (y_quarter == 1 || y_quarter == 3); + if (flip_quarter) + offset_x = quarter_cols - 1 - offset_x; + + const bool is_top_stripe = !((y % (panel_rows_ / 2)) < quarter_rows); + + // Compute matrix_x: mirrors within half panels depending on stripe position + const int base_x = 2 * x_quarter * quarter_cols; + const int top_offset = is_top_stripe + ? (quarter_cols - 1 - offset_x) + : (quarter_cols + offset_x); + + *matrix_x = base_x + top_offset; + *matrix_y = (y_quarter / 2) * quarter_rows + offset_y; + } + }; + +/* + * P4 Outdoor panel 80x40 pixels + * https://nl.aliexpress.com/item/1005003999341251.html?spm=a2g0o.order_list.order_list_main.11.408679d2q5LwTb&gatewayAdapt=glo2nld + */ +class P4Outdoor80x40Mapper : public MultiplexMapperBase { +public: + P4Outdoor80x40Mapper() : MultiplexMapperBase("P4Outdoor80x40Mapper", 2) {} + + void MapSinglePanel(int x, int y, int *matrix_x, int *matrix_y) const { + + const int tile_width_ = 8; + const int tile_height_ = 10; + const int vblock_is_odd = (y / tile_height_) % 2; + const int hblock = x / tile_width_; + + if (vblock_is_odd) { + *matrix_x = (x % tile_width_) + (2 * tile_width_ * hblock) + tile_width_; + } else { + // even tiles have reverse x-order + *matrix_x = -((x % tile_width_) - tile_width_ + 1) + (2 * tile_width_ * hblock); + } + *matrix_y = (y % tile_height_) + tile_height_ * (y / (tile_height_ * 2)); + } +}; + +/* + * Here is where the registration happens. + * If you add an instance of the mapper here, it will automatically be + * made available in the --led-multiplexing commandline option. + */ +static MuxMapperList *CreateMultiplexMapperList() { + MuxMapperList *result = new MuxMapperList(); + + // Here, register all multiplex mappers from above. + result->push_back(new StripeMultiplexMapper()); + result->push_back(new CheckeredMultiplexMapper()); + result->push_back(new SpiralMultiplexMapper()); + result->push_back(new ZStripeMultiplexMapper("ZStripe", 0, 8)); + result->push_back(new ZStripeMultiplexMapper("ZnMirrorZStripe", 4, 4)); + result->push_back(new CoremanMapper()); + result->push_back(new Kaler2ScanMapper()); + result->push_back(new ZStripeMultiplexMapper("ZStripeUneven", 8, 0)); + result->push_back(new P10MapperZ()); + result->push_back(new QiangLiQ8()); + result->push_back(new InversedZStripe()); + result->push_back(new P10Outdoor1R1G1BMultiplexMapper1()); + result->push_back(new P10Outdoor1R1G1BMultiplexMapper2()); + result->push_back(new P10Outdoor1R1G1BMultiplexMapper3()); + result->push_back(new P10CoremanMapper()); + result->push_back(new P8Outdoor1R1G1BMultiplexMapper()); + result->push_back(new FlippedStripeMultiplexMapper()); + result->push_back(new P10Outdoor32x16HalfScanMapper()); + result->push_back(new P10Outdoor32x16QuarterScanMapper()); + result->push_back(new P3Outdoor64x64MultiplexMapper()); + result->push_back(new DoubleZMultiplexMapper()); + result->push_back(new P4Outdoor80x40Mapper()); + return result; +} + +const MuxMapperList &GetRegisteredMultiplexMappers() { + static const MuxMapperList *all_mappers = CreateMultiplexMapperList(); + return *all_mappers; +} +} // namespace internal +} // namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/options-initialize.cc b/deps/rpi-rgb-led-matrix/lib/options-initialize.cc new file mode 100644 index 000000000000..8601995c28f0 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/options-initialize.cc @@ -0,0 +1,507 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013, 2016 Henner Zeller +// +// 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. +// +// 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, see + +#include "led-matrix.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "multiplex-mappers-internal.h" +#include "framebuffer-internal.h" + +#include "gpio.h" + +namespace rgb_matrix { +RuntimeOptions::RuntimeOptions() : +#ifdef RGB_SLOWDOWN_GPIO + gpio_slowdown(RGB_SLOWDOWN_GPIO), +#else + gpio_slowdown(GPIO::IsPi4() ? 2 : 1), +#endif + rp1_pio(0), + daemon(0), // Don't become a daemon by default. + drop_privileges(1), // Encourage good practice: drop privileges by default. + do_gpio_init(true), + drop_priv_user("daemon"), + drop_priv_group("daemon") +{ + // Nothing to see here. +} + +namespace { +typedef char** argv_iterator; + +#define OPTION_PREFIX "--led-" +#define OPTION_PREFIX_LEN strlen(OPTION_PREFIX) + +static bool ConsumeBoolFlag(const char *flag_name, const argv_iterator &pos, + bool *result_value) { + const char *option = *pos; + if (strncmp(option, OPTION_PREFIX, OPTION_PREFIX_LEN) != 0) + return false; + option += OPTION_PREFIX_LEN; + bool value_to_set = true; + if (strncmp(option, "no-", 3) == 0) { + value_to_set = false; + option += 3; + } + if (strcmp(option, flag_name) != 0) + return false; // not consumed. + *result_value = value_to_set; + return true; +} + +static bool ConsumeIntFlag(const char *flag_name, + argv_iterator &pos, const argv_iterator end, + int *result_value, int *error) { + const char *option = *pos; + if (strncmp(option, OPTION_PREFIX, OPTION_PREFIX_LEN) != 0) + return false; + option += OPTION_PREFIX_LEN; + const size_t flag_len = strlen(flag_name); + if (strncmp(option, flag_name, flag_len) != 0) + return false; // not consumed. + const char *value; + if (option[flag_len] == '=') // --option=42 # value in same arg + value = option + flag_len + 1; + else if (pos + 1 < end) { // --option 42 # value in next arg + value = *(++pos); + } else { + fprintf(stderr, "Parameter expected after %s%s\n", + OPTION_PREFIX, flag_name); + ++*error; + return true; // consumed, but error. + } + char *end_value = NULL; + int val = strtol(value, &end_value, 10); + if (!*value || *end_value) { + fprintf(stderr, "Couldn't parse parameter %s%s=%s " + "(Expected decimal number but '%s' looks funny)\n", + OPTION_PREFIX, flag_name, value, end_value); + ++*error; + return true; // consumed, but error + } + *result_value = val; + return true; // consumed. +} + +// The resulting value is allocated. +static bool ConsumeStringFlag(const char *flag_name, + argv_iterator &pos, const argv_iterator end, + const char **result_value, int *error) { + const char *option = *pos; + if (strncmp(option, OPTION_PREFIX, OPTION_PREFIX_LEN) != 0) + return false; + option += OPTION_PREFIX_LEN; + const size_t flag_len = strlen(flag_name); + if (strncmp(option, flag_name, flag_len) != 0) + return false; // not consumed. + const char *value; + if (option[flag_len] == '=') // --option=hello # value in same arg + value = option + flag_len + 1; + else if (pos + 1 < end) { // --option hello # value in next arg + value = *(++pos); + } else { + fprintf(stderr, "Parameter expected after %s%s\n", + OPTION_PREFIX, flag_name); + ++*error; + *result_value = NULL; + return true; // consumed, but error. + } + *result_value = strdup(value); // This will leak, but no big deal. + return true; +} + +static bool FlagInit(int &argc, char **&argv, + RGBMatrix::Options *mopts, + RuntimeOptions *ropts, + bool remove_consumed_options) { + argv_iterator it = &argv[0]; + argv_iterator end = it + argc; + + std::vector unused_options; + unused_options.push_back(*it++); // Not interested in program name + + bool bool_scratch; + int err = 0; + bool posix_end_option_seen = false; // end of options '--' + for (/**/; it < end; ++it) { + posix_end_option_seen |= (strcmp(*it, "--") == 0); + if (!posix_end_option_seen) { + if (ConsumeStringFlag("gpio-mapping", it, end, + &mopts->hardware_mapping, &err)) + continue; + if (ConsumeStringFlag("rgb-sequence", it, end, + &mopts->led_rgb_sequence, &err)) + continue; + if (ConsumeStringFlag("pixel-mapper", it, end, + &mopts->pixel_mapper_config, &err)) + continue; + if (ConsumeStringFlag("panel-type", it, end, + &mopts->panel_type, &err)) + continue; + if (ConsumeIntFlag("rows", it, end, &mopts->rows, &err)) + continue; + if (ConsumeIntFlag("cols", it, end, &mopts->cols, &err)) + continue; + if (ConsumeIntFlag("chain", it, end, &mopts->chain_length, &err)) + continue; + if (ConsumeIntFlag("parallel", it, end, &mopts->parallel, &err)) + continue; + if (ConsumeIntFlag("multiplexing", it, end, &mopts->multiplexing, &err)) + continue; + if (ConsumeIntFlag("brightness", it, end, &mopts->brightness, &err)) + continue; + if (ConsumeIntFlag("scan-mode", it, end, &mopts->scan_mode, &err)) + continue; + if (ConsumeIntFlag("pwm-bits", it, end, &mopts->pwm_bits, &err)) + continue; + if (ConsumeIntFlag("pwm-lsb-nanoseconds", it, end, + &mopts->pwm_lsb_nanoseconds, &err)) + continue; + if (ConsumeIntFlag("pwm-dither-bits", it, end, + &mopts->pwm_dither_bits, &err)) + continue; + if (ConsumeIntFlag("row-addr-type", it, end, + &mopts->row_address_type, &err)) + continue; + if (ConsumeIntFlag("limit-refresh", it, end, + &mopts->limit_refresh_rate_hz, &err)) + continue; + if (ConsumeBoolFlag("show-refresh", it, &mopts->show_refresh_rate)) + continue; + if (ConsumeBoolFlag("inverse", it, &mopts->inverse_colors)) + continue; + // We don't have a swap_green_blue option anymore, but we simulate the + // flag for a while. + bool swap_green_blue; + if (ConsumeBoolFlag("swap-green-blue", it, &swap_green_blue)) { + if (strlen(mopts->led_rgb_sequence) == 3) { + char *new_sequence = strdup(mopts->led_rgb_sequence); + new_sequence[0] = mopts->led_rgb_sequence[0]; + new_sequence[1] = mopts->led_rgb_sequence[2]; + new_sequence[2] = mopts->led_rgb_sequence[1]; + mopts->led_rgb_sequence = new_sequence; // leaking. Ignore. + } + continue; + } + bool allow_hardware_pulsing = !mopts->disable_hardware_pulsing; + if (ConsumeBoolFlag("hardware-pulse", it, &allow_hardware_pulsing)) { + mopts->disable_hardware_pulsing = !allow_hardware_pulsing; + continue; + } + + bool allow_busy_waiting = !mopts->disable_busy_waiting; + if (ConsumeBoolFlag("busy-waiting", it, &allow_busy_waiting)) { + mopts->disable_busy_waiting = !allow_busy_waiting; + continue; + } + + bool request_help = false; + if (ConsumeBoolFlag("help", it, &request_help) && request_help) { + // In that case, we pretend to have failure in parsing, which will + // trigger printing the usage(). Typically :) + return false; + } + + //-- Runtime options. + if (ConsumeIntFlag("slowdown-gpio", it, end, &ropts->gpio_slowdown, &err)) + continue; + const int err_before_rp1_pio = err; + if (ConsumeIntFlag("rp1-pio", it, end, &ropts->rp1_pio, &err)) { + if (err == err_before_rp1_pio + && ropts->rp1_pio != 0 && ropts->rp1_pio != 1) { + fprintf(stderr, "%s%s=%d is outside usable range 0..1\n", + OPTION_PREFIX, "rp1-pio", ropts->rp1_pio); + ++err; + } + continue; + } + if (ropts->daemon >= 0 && ConsumeBoolFlag("daemon", it, &bool_scratch)) { + ropts->daemon = bool_scratch ? 1 : 0; + continue; + } + if (ropts->drop_privileges >= 0 && + ConsumeBoolFlag("drop-privs", it, &bool_scratch)) { + ropts->drop_privileges = bool_scratch ? 1 : 0; + continue; + } + if (ConsumeStringFlag("drop-priv-user", it, end, + &ropts->drop_priv_user, &err)) { + continue; + } + if (ConsumeStringFlag("drop-priv-group", it, end, + &ropts->drop_priv_group, &err)) { + continue; + } + + if (strncmp(*it, OPTION_PREFIX, OPTION_PREFIX_LEN) == 0) { + fprintf(stderr, "Option %s starts with %s but it is unknown. Typo?\n", + *it, OPTION_PREFIX); + } + } + unused_options.push_back(*it); + } + + if (err > 0) { + return false; + } + + if (remove_consumed_options) { + // Success. Re-arrange flags to only include the ones not consumed. + argc = (int) unused_options.size(); + for (int i = 0; i < argc; ++i) { + argv[i] = unused_options[i]; + } + } + return true; +} + +} // anonymous namespace + +bool ParseOptionsFromFlags(int *argc, char ***argv, + RGBMatrix::Options *m_opt_in, + RuntimeOptions *rt_opt_in, + bool remove_consumed_options) { + if (argc == NULL || argv == NULL) { + fprintf(stderr, "Called ParseOptionsFromFlags() without argc/argv\n"); + return false; + } + // Replace NULL arguments with some scratch-space. + RGBMatrix::Options scratch_matrix; + RGBMatrix::Options *mopt = (m_opt_in != NULL) ? m_opt_in : &scratch_matrix; + + RuntimeOptions scratch_rt; + RuntimeOptions *ropt = (rt_opt_in != NULL) ? rt_opt_in : &scratch_rt; + + return FlagInit(*argc, *argv, mopt, ropt, remove_consumed_options); +} + +static std::string CreateAvailableMultiplexString( + const internal::MuxMapperList &m) { + std::string result; + char buffer[256]; + for (size_t i = 0; i < m.size(); ++i) { + if (i != 0) result.append("; "); + snprintf(buffer, sizeof(buffer), "%d=%s", (int) i+1, m[i]->GetName()); + result.append(buffer); + } + return result; +} + +void PrintMatrixFlags(FILE *out, const RGBMatrix::Options &d, + const RuntimeOptions &r) { + const internal::MuxMapperList &muxers + = internal::GetRegisteredMultiplexMappers(); + + std::vector mapper_names = GetAvailablePixelMappers(); + std::string available_mappers; + for (size_t i = 0; i < mapper_names.size(); ++i) { + if (i != 0) available_mappers.append(", "); + available_mappers.append("\"").append(mapper_names[i]).append("\""); + } + + fprintf(out, + "\t--led-gpio-mapping= : Name of GPIO mapping used. Default \"%s\"\n" + "\t--led-rows= : Panel rows. Typically 8, 16, 32 or 64." + " (Default: %d).\n" + "\t--led-cols= : Panel columns. Typically 32 or 64. " + "(Default: %d).\n" + "\t--led-chain= : Number of daisy-chained panels. " + "(Default: %d).\n" + "\t--led-parallel= : Parallel chains. range=1..3 " +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + "(6 for CM3) " +#endif + "(Default: %d).\n" + "\t--led-multiplexing=<0..%d> : Mux type: 0=direct; %s (Default: 0)\n" + "\t--led-pixel-mapper : Semicolon-separated list of pixel-mappers to arrange pixels.\n" + "\t Optional params after a colon e.g. \"U-mapper;Rotate:90\"\n" + "\t Available: %s. Default: \"\"\n" + "\t--led-pwm-bits=<1..%d> : PWM bits (Default: %d).\n" + "\t--led-brightness=: Brightness in percent (Default: %d).\n" + "\t--led-scan-mode=<0..1> : 0 = progressive; 1 = interlaced " + "(Default: %d).\n" + "\t--led-row-addr-type=<0..5>: 0 = default; 1 = AB-addressed panels; 2 = direct row select; 3 = ABC-addressed panels; 4 = ABC Shift + DE direct; 5 = ABC direct " + "(Default: 0).\n" + "\t--led-%sshow-refresh : %show refresh rate.\n" + "\t--led-limit-refresh= : Limit refresh rate to this frequency in Hz. Useful to keep a\n" + "\t constant refresh rate on loaded system. 0=no limit. Default: %d\n" + "\t--led-%sinverse " + ": Switch if your matrix has inverse colors %s.\n" + "\t--led-rgb-sequence : Switch if your matrix has led colors " + "swapped (Default: \"RGB\")\n" + "\t--led-pwm-lsb-nanoseconds : PWM Nanoseconds for LSB " + "(Default: %d)\n" + "\t--led-pwm-dither-bits=<0..2> : Time dithering of lower bits " + "(Default: 0)\n" + "\t--led-%shardware-pulse : %sse hardware pin-pulse generation.\n" + "\t--led-panel-type= : Needed to initialize special panels. Supported: 'FM6126A', 'FM6127'\n" + "\t--led-%sbusy-waiting : %sse busy waiting when limiting refresh rate.\n", + d.hardware_mapping, + d.rows, d.cols, d.chain_length, d.parallel, + (int) muxers.size(), CreateAvailableMultiplexString(muxers).c_str(), + available_mappers.c_str(), + internal::Framebuffer::kBitPlanes, d.pwm_bits, + d.brightness, d.scan_mode, + d.show_refresh_rate ? "no-" : "", d.show_refresh_rate ? "Don't s" : "S", + d.limit_refresh_rate_hz, + d.inverse_colors ? "no-" : "", d.inverse_colors ? "off" : "on", + d.pwm_lsb_nanoseconds, + !d.disable_hardware_pulsing ? "no-" : "", + !d.disable_hardware_pulsing ? "Don't u" : "U", + !d.disable_busy_waiting ? "no-" : "", + !d.disable_busy_waiting ? "Don't u" : "U"); + + fprintf(out, + "\t--led-slowdown-gpio=<%d..60>: " + "Slowdown GPIO. Needed for faster Pis/slower panels " + "(Default: %d (2 on Pi4, 1 other)%s).\n", + (LED_MATRIX_ALLOW_BARRIER_DELAY ? -1 : 0), r.gpio_slowdown, + LED_MATRIX_ALLOW_BARRIER_DELAY ? "Use -1 for memory barrier approach" + : ""); + fprintf(out, + "\t--led-rp1-pio=<0|1> : On Raspberry Pi 5-family boards, force the " + "RP1 PIO backend.\n" + "\t 0=default RP1 RIO, 1=PIO (Default: %d).\n", + r.rp1_pio); + if (r.daemon >= 0) { + const bool on = (r.daemon > 0); + fprintf(out, + "\t--led-%sdaemon : " + "%sake the process run in the background as daemon.\n", + on ? "no-" : "", on ? "Don't m" : "M"); + } + if (r.drop_privileges >= 0) { + const bool on = (r.drop_privileges > 0); + fprintf(out, + "\t--led-%sdrop-privs : %srop privileges from 'root' " + "after initializing the hardware.\n", + on ? "no-" : "", on ? "Don't d" : "D"); + fprintf(out, "\t--led-drop-priv-user : " + "Drop privileges to this username or UID (Default: '%s')\n", + r.drop_priv_user); + fprintf(out, "\t--led-drop-priv-group : " + "Drop privileges to this groupname or GID (Default: '%s')\n", + r.drop_priv_group); + } +} + +bool RGBMatrix::Options::Validate(std::string *err_in) const { + std::string scratch; + std::string *err = err_in ? err_in : &scratch; + bool success = true; + if (rows < 8 || rows > 64 || rows % 2 != 0) { + err->append("Invalid number or rows per panel (--led-rows). " + "Should be in range of [8..64] and divisible by 2.\n"); + success = false; + } + + if (cols < 16) { + err->append("Invalid number of columns for panel (--led-cols). " + "Typically that is something like 32 or 64\n"); + success = false; + } + + if (chain_length < 1) { + err->append("Chain-length outside usable range.\n"); + success = false; + } + + const internal::MuxMapperList &muxers + = internal::GetRegisteredMultiplexMappers(); + if (multiplexing < 0 || multiplexing > (int)muxers.size()) { + err->append("Multiplexing can only be one of 0=normal; ") + .append(CreateAvailableMultiplexString(muxers)); + success = false; + } + + if (row_address_type < 0 || row_address_type > 5) { + err->append("Row address type values can be 0 (default), 1 (AB addressing), 2 (direct row select), 3 (ABC address), 4 (ABC Shift + DE direct), 5 (Test row select).\n"); + success = false; + } + +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + const bool is_cm = (strcmp(hardware_mapping, "compute-module") == 0); +#else + const bool is_cm = false; +#endif + if (parallel < 1 || parallel > (is_cm ? 6 : 3)) { + err->append("Parallel outside usable range (1..3 allowed" +#ifdef ENABLE_WIDE_GPIO_COMPUTE_MODULE + ", up to 6 only for CM3" +#endif + ").\n"); + success = false; + } + + if (brightness < 1 || brightness > 100) { + err->append("Brightness outside usable range (Percent 1..100 allowed).\n"); + success = false; + } + + if (pwm_bits <= 0 || pwm_bits > internal::Framebuffer::kBitPlanes) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), + "Invalid range of pwm-bits (1..%d allowed).\n", + internal::Framebuffer::kBitPlanes); + err->append(buffer); + success = false; + } + + if (scan_mode < 0 || scan_mode > 1) { + err->append("Invalid scan mode (0 or 1 allowed).\n"); + success = false; + } + + if (pwm_lsb_nanoseconds < 50 || pwm_lsb_nanoseconds > 3000) { + err->append("Invalid range of pwm-lsb-nanoseconds (50..3000 allowed).\n"); + success = false; + } + + if (pwm_dither_bits < 0 || pwm_dither_bits > 2) { + err->append("Invalid range of pwm-dither-bits (0..2 allowed).\n"); + success = false; + } + + if (led_rgb_sequence == NULL || strlen(led_rgb_sequence) != 3) { + err->append("led-sequence needs to be three characters long.\n"); + success = false; + } else { + if ((!strchr(led_rgb_sequence, 'R') && !strchr(led_rgb_sequence, 'r')) + || (!strchr(led_rgb_sequence, 'G') && !strchr(led_rgb_sequence, 'g')) + || (!strchr(led_rgb_sequence, 'B') && !strchr(led_rgb_sequence, 'b'))) { + err->append("led-sequence needs to contain all of letters 'R', 'G' " + "and 'B'\n"); + success = false; + } + } + + if (!success && !err_in) { + // If we didn't get a string to write to, we write things to stderr. + fprintf(stderr, "%s", err->c_str()); + } + + return success; +} + +} // namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/pixel-mapper.cc b/deps/rpi-rgb-led-matrix/lib/pixel-mapper.cc new file mode 100644 index 000000000000..cf3bd8917b18 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/pixel-mapper.cc @@ -0,0 +1,583 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2018 Henner Zeller +// +// 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. +// +// 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, see + +#include "pixel-mapper.h" + +#include +#include +#include +#include + +#include + +namespace rgb_matrix { +namespace { + +class RotatePixelMapper : public PixelMapper { +public: + RotatePixelMapper() : angle_(0) {} + + virtual const char *GetName() const { return "Rotate"; } + + virtual bool SetParameters(int chain, int parallel, const char *param) { + if (param == NULL || strlen(param) == 0) { + angle_ = 0; + return true; + } + char *errpos; + const int angle = strtol(param, &errpos, 10); + if (*errpos != '\0') { + fprintf(stderr, "Invalid rotate parameter '%s'\n", param); + return false; + } + if (angle % 90 != 0) { + fprintf(stderr, "Rotation needs to be multiple of 90 degrees\n"); + return false; + } + angle_ = (angle + 360) % 360; + return true; + } + + virtual bool GetSizeMapping(int matrix_width, int matrix_height, + int *visible_width, int *visible_height) + const { + if (angle_ % 180 == 0) { + *visible_width = matrix_width; + *visible_height = matrix_height; + } else { + *visible_width = matrix_height; + *visible_height = matrix_width; + } + return true; + } + + virtual void MapVisibleToMatrix(int matrix_width, int matrix_height, + int x, int y, + int *matrix_x, int *matrix_y) const { + switch (angle_) { + case 0: + *matrix_x = x; + *matrix_y = y; + break; + case 90: + *matrix_x = matrix_width - y - 1; + *matrix_y = x; + break; + case 180: + *matrix_x = matrix_width - x - 1; + *matrix_y = matrix_height - y - 1; + break; + case 270: + *matrix_x = y; + *matrix_y = matrix_height - x - 1; + break; + } + } + +private: + int angle_; +}; + +class MirrorPixelMapper : public PixelMapper { +public: + MirrorPixelMapper() : horizontal_(true) {} + + virtual const char *GetName() const { return "Mirror"; } + + virtual bool SetParameters(int chain, int parallel, const char *param) { + if (param == NULL || strlen(param) == 0) { + horizontal_ = true; + return true; + } + if (strlen(param) != 1) { + fprintf(stderr, "Mirror parameter should be a single " + "character:'V' or 'H'\n"); + } + switch (*param) { + case 'V': + case 'v': + horizontal_ = false; + break; + case 'H': + case 'h': + horizontal_ = true; + break; + default: + fprintf(stderr, "Mirror parameter should be either 'V' or 'H'\n"); + return false; + } + return true; + } + + virtual bool GetSizeMapping(int matrix_width, int matrix_height, + int *visible_width, int *visible_height) + const { + *visible_height = matrix_height; + *visible_width = matrix_width; + return true; + } + + virtual void MapVisibleToMatrix(int matrix_width, int matrix_height, + int x, int y, + int *matrix_x, int *matrix_y) const { + if (horizontal_) { + *matrix_x = matrix_width - 1 - x; + *matrix_y = y; + } else { + *matrix_x = x; + *matrix_y = matrix_height - 1 - y; + } + } + +private: + bool horizontal_; +}; + +// If we take a long chain of panels and arrange them in a U-shape, so +// that after half the panels we bend around and continue below. This way +// we have a panel that has double the height but only uses one chain. +// A single chain display with four 32x32 panels can then be arranged in this +// 64x64 display: +// [<][<][<][<] }- Raspberry Pi connector +// +// can be arranged in this U-shape +// [<][<] }----- Raspberry Pi connector +// [>][>] +// +// This works for more than one chain as well. Here an arrangement with +// two chains with 8 panels each +// [<][<][<][<] }-- Pi connector #1 +// [>][>][>][>] +// [<][<][<][<] }--- Pi connector #2 +// [>][>][>][>] +class UArrangementMapper : public PixelMapper { +public: + UArrangementMapper() : parallel_(1) {} + + virtual const char *GetName() const { return "U-mapper"; } + + virtual bool SetParameters(int chain, int parallel, const char *param) { + if (chain < 2) { // technically, a chain of 2 would work, but somewhat pointless + fprintf(stderr, "U-mapper: need at least --led-chain=4 for useful folding\n"); + return false; + } + if (chain % 2 != 0) { + fprintf(stderr, "U-mapper: Chain (--led-chain) needs to be divisible by two\n"); + return false; + } + parallel_ = parallel; + return true; + } + + virtual bool GetSizeMapping(int matrix_width, int matrix_height, + int *visible_width, int *visible_height) + const { + *visible_width = (matrix_width / 64) * 32; // Div at 32px boundary + *visible_height = 2 * matrix_height; + if (matrix_height % parallel_ != 0) { + fprintf(stderr, "%s For parallel=%d we would expect the height=%d " + "to be divisible by %d ??\n", + GetName(), parallel_, matrix_height, parallel_); + return false; + } + return true; + } + + virtual void MapVisibleToMatrix(int matrix_width, int matrix_height, + int x, int y, + int *matrix_x, int *matrix_y) const { + const int panel_height = matrix_height / parallel_; + const int visible_width = (matrix_width / 64) * 32; + const int slab_height = 2 * panel_height; // one folded u-shape + const int base_y = (y / slab_height) * panel_height; + y %= slab_height; + if (y < panel_height) { + x += matrix_width / 2; + } else { + x = visible_width - x - 1; + y = slab_height - y - 1; + } + *matrix_x = x; + *matrix_y = base_y + y; + } + +private: + int parallel_; +}; + + + +class VerticalMapper : public PixelMapper { +public: + VerticalMapper() {} + + virtual const char *GetName() const { return "V-mapper"; } + + virtual bool SetParameters(int chain, int parallel, const char *param) { + chain_ = chain; + parallel_ = parallel; + // optional argument :Z allow for every other panel to be flipped + // upside down so that cabling can be shorter: + // [ O < I ] without Z [ O < I ] + // ,---^ <---- ^ + // [ O < I ] [ I > O ] + // ,---^ with Z ^ + // [ O < I ] ---> [ O < I ] + z_ = (param && strcasecmp(param, "Z") == 0); + return true; + } + + virtual bool GetSizeMapping(int matrix_width, int matrix_height, + int *visible_width, int *visible_height) + const { + *visible_width = matrix_width * parallel_ / chain_; + *visible_height = matrix_height * chain_ / parallel_; +#if 0 + fprintf(stderr, "%s: C:%d P:%d. Turning W:%d H:%d Physical " + "into W:%d H:%d Virtual\n", + GetName(), chain_, parallel_, + *visible_width, *visible_height, matrix_width, matrix_height); +#endif + return true; + } + + virtual void MapVisibleToMatrix(int matrix_width, int matrix_height, + int x, int y, + int *matrix_x, int *matrix_y) const { + const int panel_width = matrix_width / chain_; + const int panel_height = matrix_height / parallel_; + // because the panel you plug into ends up being the "bottom" panel and coordinates + // start from the top panel, and you typically don't wire the bottom panel (first in + // the chain) upside down, whether each panel gets swapped depends on this. + // Without this, if you wire for 4 panels high and add a 5h panel, without this + // code everything would get reversed and you'd have to re-layout all the panels + bool is_height_even_panels = ( matrix_width / panel_width) % 2; + const int x_panel_start = y / panel_height * panel_width; + const int y_panel_start = x / panel_width * panel_height; + const int x_within_panel = x % panel_width; + const int y_within_panel = y % panel_height; + const bool needs_flipping = z_ && (is_height_even_panels - ((y / panel_height) % 2)) == 0; + *matrix_x = x_panel_start + (needs_flipping + ? panel_width - 1 - x_within_panel + : x_within_panel); + *matrix_y = y_panel_start + (needs_flipping + ? panel_height - 1 - y_within_panel + : y_within_panel); + } + +private: + bool z_; + int chain_; + int parallel_; +}; + +// Takes a vertically stacked parallel strands and display rearranges it into a +// horizontal row of the same bands, optionally flipping every other band for +// symmetry. +// +// Parameters (optional): +// - "Z": flip every other band (serpentine) +// - "F": flip the right half by 180° (for windmill symmetry) +class StackToRowMapper : public PixelMapper { +public: + StackToRowMapper() : z_(false), flip_right_(false), bands_(2) {} + + virtual const char *GetName() const { return "StackToRow"; } + + virtual bool SetParameters(int chain, int parallel, const char *param) { + bands_ = parallel; + z_ = false; + flip_right_ = false; + if (param && *param) { + for (const char *p = param; *p; ++p) { + const char c = *p; + if (c == ',' || c == ' ') + continue; + switch (c) { + case 'Z': case 'z': z_ = true; break; + case 'F': case 'f': flip_right_ = true; break; + default: + fprintf(stderr, "StackToRow: unknown parameter '%c' (use Z and/or F)\n", c); + return false; + } + } + } + return true; + } + + virtual bool GetSizeMapping(int matrix_width, int matrix_height, + int *visible_width, int *visible_height) const { + *visible_width = matrix_width * bands_; + *visible_height = matrix_height / bands_; + return true; + } + + virtual void MapVisibleToMatrix(int matrix_width, int matrix_height, int x, int y, + int *matrix_x, int *matrix_y) const { + const int band_width = matrix_width; + const int band_height = matrix_height / bands_; + const int band = x / band_width; + const int x_in_band = x % band_width; + const int y_in_band = y; + + int src_band = band; + int src_x = x_in_band; + int src_y = y_in_band; + + // Optionally flip every other band (serpentine) + if (z_ && (band % 2 == 1)) { + src_x = band_width - 1 - src_x; + src_y = band_height - 1 - src_y; + } + + // Optionally flip the right half by 180deg + if (flip_right_ && band >= bands_ / 2) { + src_x = band_width - 1 - src_x; + src_y = band_height - 1 - src_y; + } + + *matrix_x = src_x; + *matrix_y = src_band * band_height + src_y; + } + +private: + bool z_; + bool flip_right_; + int bands_; +}; + +class RemapMapper : public PixelMapper { +public: + RemapMapper() {} + + virtual const char *GetName() const { return "Remap"; } + + virtual bool SetParameters(int chain, int parallel, const char *param) { + chain_ = chain; + parallel_ = parallel; + const char* pos = param; + if (!pos || !*pos) { + fprintf(stderr, "Remap mapper can't be used without parameters\n"); + return false; + } + width_ = strtol(pos, (char**)&pos, 10); + if (*pos++ != ',') { + fprintf(stderr, "expected ',' after width parameter ('%s')\n", param); + return false; + } + height_ = strtol(pos, (char**)&pos, 10); + if (*pos++ != '|') { + fprintf(stderr, "expected '|' after height parameter ('%s')\n", param); + return false; + } + while(*pos) { + MapTile tile; + if ((pos = tile.ParseParam(pos)) == NULL) + return false; + map_.push_back(tile); + if (*pos == '|') { + ++pos; + continue; + } + if (*pos) { + fprintf(stderr, "Expected '|' tile separator after panel %d '%s'\n", (int)map_.size(), param); + return false; + } + } + if ((int)map_.size() != chain * parallel) { + fprintf(stderr, "Remap list must have 1 entry for each panel (got %d, expected %d)\n", (int)map_.size(), chain * parallel); + return false; + } + return true; + } + + virtual bool GetSizeMapping(int matrix_width, int matrix_height, + int *visible_width, int *visible_height) + const { + // need to go through mapping here to handle rectangular rotated panels + const int panel_width = matrix_width / chain_; + const int panel_height = matrix_height / parallel_; + // check if all panels are at least partially inside visible area + for (size_t i = 0; i < map_.size(); i++) { + int x0 = -1, y0 = -1, x1 = -1, y1 = -1; + // map opposite corners to get panel placement + if (!map_[i].MapToVisible(panel_width, panel_height, 0, 0, &x0, &y0)) continue; + if (!map_[i].MapToVisible(panel_width, panel_height, panel_width - 1, panel_height - 1, &x1, &y1)) continue; + if (x1 < 0 || x0 >= width_ || y1 < 0 || y0 >= height_) { + fprintf(stderr, "Panel %d is fully outside of visible area [%d,%d-%d,%d]", (int)i, x0, y0, x1, y1); + return false; + } + } + *visible_width = width_; + *visible_height = height_; +#if 0 + fprintf(stderr, "%s: C:%d P:%d. Turning W:%d H:%d Physical " + "into W:%d H:%d Virtual\n", + GetName(), chain_, parallel_, + *visible_width, *visible_height, matrix_width, matrix_height); +#endif + return true; + } + + virtual MappingType GetMappingType() const { return MatrixToVisible; } + virtual void MapVisibleToMatrix(int matrix_width, int matrix_height, + int x, int y, + int *matrix_x, int *matrix_y) const { + return; + } + virtual bool MapMatrixToVisible(int matrix_width, int matrix_height, + int x, int y, + int *visible_x, int *visible_y) const { + const int panel_width = matrix_width / chain_; + const int panel_height = matrix_height / parallel_; + + const int panel_col = chain_ - x / panel_width - 1; // count panels from Raspberry (first panel is top-right corner) + const int panel_row = y / panel_height; + + const int x_within_panel = x % panel_width; + const int y_within_panel = y % panel_height; + + const MapTile& tile = map_[panel_row * chain_ + panel_col]; + + int new_x = -1, new_y = -1; + if (tile.MapToVisible(panel_width, panel_height, x_within_panel, y_within_panel, &new_x, &new_y)) { + if (new_x >= 0 && new_x < width_ + && new_y >=0 && new_y < height_) { + *visible_x = new_x; + *visible_y = new_y; + return true; + } + } + return false; + } + +private: + struct MapTile { + const char* ParseParam(const char* param) { + const char *pos = param; + const char *end = strchrnul(param, '|'); + x_ = strtol(pos, (char**)&pos, 10); + if (*pos++ != ',') { + fprintf(stderr, "expected ',' after x parameter '%.*s'\n", (int)(end-param), param); + return NULL; + } + y_ = strtol(pos, (char**)&pos, 10); + switch (tolower(*pos++)) { + case 'n': type_ = rot0; break; + case 's': type_ = rot180; break; + case 'e': type_ = rot270; break; + case 'w': type_ = rot90; break; + case 'x': type_ = discard; break; + default: + fprintf(stderr, "Expected orientation (one of [neswx]) after y parameter '%.*s'\n", (int)(end-param), param); + return NULL; + } + return pos; + return NULL; + } + bool MapToVisible(int tile_width, int tile_height, int x, int y, int* new_x, int* new_y) const { + switch(type_) { + case discard: + return false; + case rot0: + *new_x = x_ + x; + *new_y = y_ + y; + return true; + case rot90: // mapping direction is opposite to Rotate pixel mapper + *new_x = x_ + y; + *new_y = y_ + tile_width - x - 1; + return true; + case rot180: + *new_x = x_ + tile_width - x - 1; + *new_y = y_ + tile_height - y - 1; + return true; + case rot270: + *new_x = x_ + tile_height - y - 1; + *new_y = y_ + x; + return true; + } + return false; + } + enum { discard, rot0, rot90, rot180, rot270 } type_; + int x_, y_; + }; + int width_; + int height_; + std::vector map_; + int chain_; + int parallel_; +}; + + +typedef std::map MapperByName; +static void RegisterPixelMapperInternal(MapperByName *registry, + PixelMapper *mapper) { + assert(mapper != NULL); + std::string lower_name; + for (const char *n = mapper->GetName(); *n; n++) + lower_name.append(1, tolower(*n)); + (*registry)[lower_name] = mapper; +} + +static MapperByName *CreateMapperMap() { + MapperByName *result = new MapperByName(); + + // Register all the default PixelMappers here. + RegisterPixelMapperInternal(result, new RotatePixelMapper()); + RegisterPixelMapperInternal(result, new UArrangementMapper()); + RegisterPixelMapperInternal(result, new VerticalMapper()); + RegisterPixelMapperInternal(result, new StackToRowMapper()); + RegisterPixelMapperInternal(result, new MirrorPixelMapper()); + RegisterPixelMapperInternal(result, new RemapMapper()); + return result; +} + +static MapperByName *GetMapperMap() { + static MapperByName *singleton_instance = CreateMapperMap(); + return singleton_instance; +} +} // anonymous namespace + +// Public API. +void RegisterPixelMapper(PixelMapper *mapper) { + RegisterPixelMapperInternal(GetMapperMap(), mapper); +} + +std::vector GetAvailablePixelMappers() { + std::vector result; + MapperByName *m = GetMapperMap(); + for (MapperByName::const_iterator it = m->begin(); it != m->end(); ++it) { + result.push_back(it->second->GetName()); + } + return result; +} + +const PixelMapper *FindPixelMapper(const char *name, + int chain, int parallel, + const char *parameter) { + std::string lower_name; + for (const char *n = name; *n; n++) lower_name.append(1, tolower(*n)); + MapperByName::const_iterator found = GetMapperMap()->find(lower_name); + if (found == GetMapperMap()->end()) { + fprintf(stderr, "%s: no such mapper\n", name); + return NULL; + } + PixelMapper *mapper = found->second; + if (mapper == NULL) return NULL; // should not happen. + if (!mapper->SetParameters(chain, parallel, parameter)) + return NULL; // Got parameter, but couldn't deal with it. + return mapper; +} +} // namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_backend.cc b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_backend.cc new file mode 100644 index 000000000000..8641343f93b4 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_backend.cc @@ -0,0 +1,770 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +#include "rp1_pio_backend.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../framebuffer-internal.h" +#include "../gpio.h" +#include "../hardware-mapping.h" + +extern "C" { +#include "hardware/pio.h" +#include "piolib.h" +} + +#include "piomatter/protomatter.pio.h" + +namespace rgb_matrix { +namespace internal { +namespace { + +// Pi 5 PIO path: +// - framebuffer.cc still decides row order and prepares one GPIO bitmap per +// pixel clock in the software framebuffer. +// - this backend repackages those GPIO bitmaps into a compact command stream +// for a single RP1 PIO state machine. +// - the PIO program owns the clock edge timing via side-set; the CPU only +// describes which GPIO levels to present and how long each bitplane stays on. + +static const uint32_t kPioOutputPinCount = 28; +static const uint32_t kMaxTransferBytes = 2 * 1024 * 1024; +// Command words use bit 31 as a tag: +// 1 = "the next N words are GPIO samples to clock out" +// 0 = "hold this GPIO sample for N encoded delay cycles" +static const uint32_t kCommandData = 1u << 31; +static const int kDelayOverheadClocks = 5; +static const int kClocksPerDataWord = 2; +static const int kPostAddressDelayClocks = 5; +static const double kBaseTargetPioClockHz = 27000000.0; + +struct Rp1PioState { + Rp1PioState() + : active(false), + panel_init_warned(false), + pio(NULL), + sm(-1), + row_address_type(0), + double_rows(0), + gpio_slowdown(1), + output_enable_bit(0), + latch_bit(0), + used_mask(0) { + } + + bool active; + bool panel_init_warned; + PIO pio; + int sm; + // Cached configuration copied from the selected matrix mapping so the hot + // dump loop does not need to rediscover backend-specific details each frame. + int row_address_type; + int double_rows; + int gpio_slowdown; + uint32_t output_enable_bit; + uint32_t latch_bit; + uint32_t used_mask; + std::vector bitplane_active_words; + std::vector transfer_buffer; +}; + +Rp1PioState s_pio_state; + +static double TargetPioClockHz() { + // Chained panels have tighter setup/hold margin; reuse the public slowdown + // flag as a Pi 5 PIO pixel-clock divisor. + const int divisor = std::max(1, s_pio_state.gpio_slowdown); + return kBaseTargetPioClockHz / divisor; +} + +static bool ReadSmallTextFile(const char *path, std::string *result) { + const int fd = open(path, O_RDONLY); + if (fd < 0) return false; + + char buffer[256]; + const ssize_t len = read(fd, buffer, sizeof(buffer) - 1); + close(fd); + if (len <= 0) return false; + + buffer[len] = '\0'; + result->assign(buffer, buffer + len); + return true; +} + +static bool FileExists(const char *path) { + struct stat st; + return stat(path, &st) == 0; +} + +static bool MappingNameSupported(const char *hardware_mapping) { + if (hardware_mapping == NULL || *hardware_mapping == '\0') { + hardware_mapping = "regular"; + } + // Keep this intentionally narrower than the generic library mapping list. + // The PIO backend assumes one clock side-set pin and RP1 GPIOs 0..27 only. + return strcasecmp(hardware_mapping, "regular") == 0 + || strcasecmp(hardware_mapping, "regular-pi1") == 0 + || strcasecmp(hardware_mapping, "classic") == 0 + || strcasecmp(hardware_mapping, "adafruit-hat") == 0 + || strcasecmp(hardware_mapping, "adafruit-hat-pwm") == 0; +} + +static uint32_t CollectColorMask(const HardwareMapping &h, int parallel) { + uint32_t mask = 0; + mask |= h.p0_r1 | h.p0_g1 | h.p0_b1 | h.p0_r2 | h.p0_g2 | h.p0_b2; + if (parallel >= 2) { + mask |= h.p1_r1 | h.p1_g1 | h.p1_b1 | h.p1_r2 | h.p1_g2 | h.p1_b2; + } + if (parallel >= 3) { + mask |= h.p2_r1 | h.p2_g1 | h.p2_b1 | h.p2_r2 | h.p2_g2 | h.p2_b2; + } + if (parallel >= 4) { + mask |= h.p3_r1 | h.p3_g1 | h.p3_b1 | h.p3_r2 | h.p3_g2 | h.p3_b2; + } + if (parallel >= 5) { + mask |= h.p4_r1 | h.p4_g1 | h.p4_b1 | h.p4_r2 | h.p4_g2 | h.p4_b2; + } + if (parallel >= 6) { + mask |= h.p5_r1 | h.p5_g1 | h.p5_b1 | h.p5_r2 | h.p5_g2 | h.p5_b2; + } + return mask; +} + +static uint32_t CollectAddressMask(const HardwareMapping &h, int double_rows, + int row_address_type) { + switch (row_address_type) { + case 0: { + uint32_t mask = h.a; + if (double_rows > 2) mask |= h.b; + if (double_rows > 4) mask |= h.c; + if (double_rows > 8) mask |= h.d; + if (double_rows > 16) mask |= h.e; + return mask; + } + case 1: + return h.a | h.b; + case 2: + return h.a | h.b | h.c | h.d; + case 3: + return h.a | h.c; + case 4: { + uint32_t mask = h.a | h.b | h.c; + if (double_rows > 8) mask |= h.d; + if (double_rows > 16) mask |= h.e; + return mask; + } + case 5: + return h.a | h.b | h.c; + default: + return 0; + } +} + +static uint32_t BuildUsedMask(const HardwareMapping &h, int double_rows, + int parallel, int row_address_type) { + uint32_t mask = h.output_enable | h.clock | h.strobe; + mask |= CollectAddressMask(h, double_rows, row_address_type); + mask |= CollectColorMask(h, parallel); + return mask; +} + +static bool FitsInPioWord(uint64_t bits) { + const uint64_t kAllowedMask = (1ull << kPioOutputPinCount) - 1; + return (bits & ~kAllowedMask) == 0; +} + +static bool IsSingleBit(uint64_t bits) { + return bits != 0 && (bits & (bits - 1)) == 0; +} + +static bool Rp1UsesShiftRegisterRowAddress(int row_address_type) { + return row_address_type == 1 || row_address_type == 3 + || row_address_type == 4 || row_address_type == 5; +} + +static bool Rp1ShiftsRowAddressEveryBitplane(int row_address_type) { + // The legacy ABC row-address setter does not cache last_row_; keep that + // timing for row-address type 3 while the other shift-register types update + // once per display row. + return row_address_type == 3; +} + +static uint32_t Rp1ShiftRegisterIdleBits(const HardwareMapping &h, int row) { + return h.a | (row == 0 ? 0 : h.b); +} + +static uint32_t Rp1AbcShiftRegisterIdleBits(const HardwareMapping &h, int row) { + return row == 0 ? h.c : 0; +} + +static uint32_t Rp1Sm5266DirectAddressBits(const HardwareMapping &h, int row) { + uint32_t bits = 0; + if (row & 0x08) bits |= h.d; + if (row & 0x10) bits |= h.e; + return bits; +} + +static uint32_t Rp1B707ShiftRegisterIdleBits(const HardwareMapping &h, + int row) { + return row == 0 ? h.c : 0; +} + +static uint32_t CalcRowAddressBits(const HardwareMapping &h, + int row_address_type, int row) { + switch (row_address_type) { + case 0: { + uint32_t bits = 0; + if (row & 0x01) bits |= h.a; + if (row & 0x02) bits |= h.b; + if (row & 0x04) bits |= h.c; + if (row & 0x08) bits |= h.d; + if (row & 0x10) bits |= h.e; + return bits; + } + case 1: + return Rp1ShiftRegisterIdleBits(h, row); + case 2: + switch (row & 0x03) { + case 0: return h.b | h.c | h.d; + case 1: return h.a | h.c | h.d; + case 2: return h.a | h.b | h.d; + default: return h.a | h.b | h.c; + } + case 3: + return Rp1AbcShiftRegisterIdleBits(h, row); + case 4: + return Rp1Sm5266DirectAddressBits(h, row); + case 5: + return Rp1B707ShiftRegisterIdleBits(h, row); + default: + return 0; + } +} + +static int DisplayRowFromLoop(int row_loop, int double_rows, int scan_mode) { + if (scan_mode != 1) return row_loop; + + const int half_double = double_rows / 2; + return (row_loop < half_double) + ? (row_loop << 1) + : (((row_loop - half_double) << 1) + 1); +} + +static void AppendDelay(std::vector *buffer, uint32_t pins, + int clock_cycles) { + int encoded_cycles = clock_cycles - kDelayOverheadClocks; + if (encoded_cycles < 1) encoded_cycles = 1; + buffer->push_back(static_cast(encoded_cycles - 1)); + buffer->push_back(pins); +} + +static void AppendDataHeader(std::vector *buffer, int words) { + buffer->push_back(kCommandData | static_cast(words - 1)); +} + +static void Rp1PioAppendShiftRegisterRowAddress( + std::vector *buffer, const HardwareMapping &h, int double_rows, + int row, uint32_t blank) { + const uint32_t clock = h.a; + const uint32_t data = h.b; + uint32_t row_bits = 0; + + for (int activate = 0; activate < double_rows; ++activate) { + AppendDelay(buffer, blank, 0); + + row_bits = (activate == double_rows - 1 - row) ? 0 : data; + AppendDelay(buffer, blank | row_bits, 0); + AppendDelay(buffer, blank | row_bits | clock, 0); + } + + AppendDelay(buffer, blank | row_bits, 0); + AppendDelay(buffer, blank | row_bits | clock, 0); +} + +static void Rp1PioAppendAbcShiftRegisterRowAddress( + std::vector *buffer, const HardwareMapping &h, int double_rows, + int row, uint32_t blank) { + const uint32_t clock = h.a; + const uint32_t data = h.c; + uint32_t row_bits = 0; + + for (int activate = 0; activate < double_rows; ++activate) { + AppendDelay(buffer, blank, 0); + + row_bits = (activate == double_rows - 1 - row) ? data : 0; + AppendDelay(buffer, blank | row_bits, 0); + AppendDelay(buffer, blank | row_bits | clock, 0); + } + + AppendDelay(buffer, blank | row_bits, 0); +} + +static void Rp1PioAppendSm5266RowAddress( + std::vector *buffer, const HardwareMapping &h, int row, + uint32_t blank) { + const uint32_t clock = h.a; + const uint32_t data = h.b; + const uint32_t enable = h.c; + + AppendDelay(buffer, blank | enable, 0); + for (int r = 7; r >= 0; --r) { + const uint32_t row_bits = ((row % 8) == r) ? data : 0; + AppendDelay(buffer, blank | enable | row_bits, 0); + AppendDelay(buffer, blank | enable | row_bits | clock, 0); + AppendDelay(buffer, blank | enable | row_bits | clock, 0); + AppendDelay(buffer, blank | enable | row_bits, 0); + } + + AppendDelay(buffer, blank | Rp1Sm5266DirectAddressBits(h, row), + kPostAddressDelayClocks); +} + +static void Rp1PioAppendB707ShiftRegisterRowAddress( + std::vector *buffer, const HardwareMapping &h, int row, + uint32_t blank) { + const uint32_t clock = h.a; + const uint32_t enable = h.b; + const uint32_t row_bits = Rp1B707ShiftRegisterIdleBits(h, row); + + AppendDelay(buffer, blank | enable, 0); + AppendDelay(buffer, blank | enable | row_bits, 0); + AppendDelay(buffer, blank | enable | row_bits | clock, 0); + AppendDelay(buffer, blank | enable | row_bits | clock, 0); + AppendDelay(buffer, blank | row_bits, 0); +} + +static void Rp1PioAppendRowAddress( + std::vector *buffer, const HardwareMapping &h, int double_rows, + int row_address_type, int row, uint32_t blank) { + switch (row_address_type) { + case 1: + Rp1PioAppendShiftRegisterRowAddress(buffer, h, double_rows, row, blank); + break; + case 3: + Rp1PioAppendAbcShiftRegisterRowAddress(buffer, h, double_rows, row, blank); + break; + case 4: + Rp1PioAppendSm5266RowAddress(buffer, h, row, blank); + break; + case 5: + Rp1PioAppendB707ShiftRegisterRowAddress(buffer, h, row, blank); + break; + default: + break; + } +} + +static int TransferLarge(PIO pio, int sm, const uint32_t *data, + size_t data_bytes) { + // Keep normal frames in one transfer. Very large frame updates are still + // streamed in chunks. + while (data_bytes > 0) { + const size_t chunk = std::min(data_bytes, + static_cast(kMaxTransferBytes)); + int rc = pio_sm_xfer_data(pio, sm, PIO_DIR_TO_SM, chunk, + const_cast(data)); + if (rc != 0) return rc; + data += chunk / sizeof(*data); + data_bytes -= chunk; + } + return 0; +} + +static void InitPinDirection(PIO pio, int sm, uint32_t used_mask) { + for (uint32_t pin = 0; pin < kPioOutputPinCount; ++pin) { + if ((used_mask & (1u << pin)) == 0) continue; + pio_gpio_init(pio, pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); + } +} + +static void ReleasePinDirection(PIO pio, int sm, uint32_t used_mask) { + for (uint32_t pin = 0; pin < kPioOutputPinCount; ++pin) { + if ((used_mask & (1u << pin)) == 0) continue; + pio_gpio_init(pio, pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + } +} + +static void PrepareBitplaneTimings(int pwm_lsb_nanoseconds, int dither_bits, + std::vector *timings_out) { + timings_out->clear(); + int timing_ns = pwm_lsb_nanoseconds; + for (int b = 0; b < Framebuffer::kBitPlanes; ++b) { + timings_out->push_back(timing_ns); + if (b >= dither_bits) timing_ns *= 2; + } +} + +static void PrepareBitplaneActiveWords(int pwm_lsb_nanoseconds, int dither_bits, + std::vector *active_words_out) { + std::vector timings_ns; + PrepareBitplaneTimings(pwm_lsb_nanoseconds, dither_bits, &timings_ns); + + const double data_word_ns = (1e9 * kClocksPerDataWord) / TargetPioClockHz(); + active_words_out->clear(); + active_words_out->reserve(timings_ns.size()); + for (size_t i = 0; i < timings_ns.size(); ++i) { + const int word_count = std::max( + 1, static_cast(ceil(static_cast(timings_ns[i]) / + data_word_ns))); + active_words_out->push_back(word_count); + } +} + +static void ConfigureStateMachineOrDie(const HardwareMapping &mapping) { + Rp1PioState &state = s_pio_state; + // One state machine drives the whole display. All GPIO samples are expanded + // on the CPU side; the PIO program's job is to issue clock edges and honor + // encoded delay commands at a stable cadence. + state.pio = pio0; + if (PIO_IS_ERR(state.pio)) { + fprintf(stderr, "Opening /dev/pio0 failed with error %d\n", + PIO_ERR_VAL(state.pio)); + abort(); + } + + state.sm = pio_claim_unused_sm(state.pio, true); + if (state.sm < 0) { + fprintf(stderr, "Could not claim an unused RP1 PIO state machine.\n"); + abort(); + } + + const int xfer_rc = pio_sm_config_xfer(state.pio, state.sm, PIO_DIR_TO_SM, + kMaxTransferBytes, 3); + if (xfer_rc != 0) { + fprintf(stderr, "RP1 PIO DMA configuration failed: %d\n", xfer_rc); + abort(); + } + + static const struct pio_program kProgram = { + protomatter, + 32, + -1, + 0, + }; + const uint offset = pio_add_program(state.pio, &kProgram); + if (offset == PIO_ORIGIN_INVALID) { + fprintf(stderr, "Loading the HUB75 RP1 PIO program failed.\n"); + abort(); + } + + pio_sm_clear_fifos(state.pio, state.sm); + pio_sm_set_clkdiv(state.pio, state.sm, 1.0f); + + pio_sm_config config = pio_get_default_sm_config(); + sm_config_set_wrap(&config, offset + protomatter_wrap_target, + offset + protomatter_wrap); + // The config API expects the full encoded side-set field width. For + // ".side_set 1 opt", that means 1 data bit plus the optional-enable bit. + const uint encoded_sideset_bits = + protomatter_sideset_pin_count + (protomatter_sideset_enable ? 1u : 0u); + sm_config_set_sideset(&config, encoded_sideset_bits, + protomatter_sideset_enable, false); + sm_config_set_out_shift(&config, false, true, 32); + sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX); + sm_config_set_clkdiv(&config, clock_get_hz(clk_sys) / TargetPioClockHz()); + sm_config_set_out_pins(&config, 0, kPioOutputPinCount); + + const unsigned clock_pin = __builtin_ctz(mapping.clock); + sm_config_set_sideset_pins(&config, clock_pin); + + pio_sm_init(state.pio, state.sm, offset, &config); + InitPinDirection(state.pio, state.sm, state.used_mask); + pio_sm_set_enabled(state.pio, state.sm, true); + pio_sm_set_pins_with_mask(state.pio, state.sm, state.output_enable_bit, + state.used_mask); +} + +static void SendRawWordsOrDie(const std::vector &pins) { + if (pins.empty()) return; + + Rp1PioState &state = s_pio_state; + // Panel init sequences reuse the same command format as the refresh loop, + // but always run with OE blanked so the panel does not flash partial data. + std::vector buffer; + buffer.reserve(pins.size() + 3); + AppendDataHeader(&buffer, pins.size()); + for (size_t i = 0; i < pins.size(); ++i) { + buffer.push_back(pins[i] | state.output_enable_bit); + } + AppendDelay(&buffer, state.output_enable_bit, 0); + + const int rc = TransferLarge(state.pio, state.sm, &buffer[0], + buffer.size() * sizeof(buffer[0])); + if (rc != 0) { + fprintf(stderr, "RP1 PIO transfer failed during panel init: %d\n", rc); + abort(); + } +} + +static void SendFM6126Init(const HardwareMapping &h, int columns) { + const uint32_t bits_on = + CollectColorMask(h, 6) | static_cast(h.a); + const uint32_t bits_off = h.a; + static const char *init_b12 = "0111111111111111"; + static const char *init_b13 = "0000000001000000"; + + std::vector pins; + pins.reserve(columns * 2); + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b12[i % 16] == '0') ? bits_off : bits_on; + if (i > columns - 12) value |= h.strobe; + pins.push_back(value); + } + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b13[i % 16] == '0') ? bits_off : bits_on; + if (i > columns - 13) value |= h.strobe; + pins.push_back(value); + } + SendRawWordsOrDie(pins); +} + +static void SendFM6127Init(const HardwareMapping &h, int columns) { + const uint32_t bits_r_on = h.p0_r1 | h.p0_r2; + const uint32_t bits_g_on = h.p0_g1 | h.p0_g2; + const uint32_t bits_b_on = h.p0_b1 | h.p0_b2; + const uint32_t bits_on = bits_r_on | bits_g_on | bits_b_on; + static const char *init_b12 = "1111111111001110"; + static const char *init_b13 = "1110000001100010"; + static const char *init_b11 = "0101111100000000"; + + std::vector pins; + pins.reserve(columns * 3); + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b12[i % 16] == '0') ? 0 : bits_on; + if (i > columns - 12) value |= h.strobe; + pins.push_back(value); + } + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b13[i % 16] == '0') ? 0 : bits_on; + if (i > columns - 13) value |= h.strobe; + pins.push_back(value); + } + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b11[i % 16] == '0') ? 0 : bits_on; + if (i > columns - 11) value |= h.strobe; + pins.push_back(value); + } + SendRawWordsOrDie(pins); +} + +} // namespace + +bool Rp1PioPlatformDetected() { + if (!GPIO::IsPi5Family()) { + std::string model; + if (!ReadSmallTextFile("/proc/device-tree/model", &model)) { + return false; + } + if (model.find("Raspberry Pi 5") == std::string::npos + && model.find("Compute Module 5") == std::string::npos) { + return false; + } + } + return FileExists("/dev/pio0"); +} + +bool Rp1PioConfigSupported(const char *hardware_mapping, int row_address_type, + int parallel) { + if (!Rp1PioPlatformDetected()) return false; + if (!MappingNameSupported(hardware_mapping)) return false; + if (!(row_address_type >= 0 && row_address_type <= 5)) return false; + return parallel >= 1 && parallel <= 3; +} + +bool Rp1PioShouldActivate(const char *hardware_mapping, int row_address_type, + int parallel) { + return Rp1PioConfigSupported(hardware_mapping, row_address_type, parallel); +} + +void Rp1PioSetGpioSlowdown(int slowdown) { + s_pio_state.gpio_slowdown = slowdown <= 1 ? 1 : slowdown; +} + +bool Rp1PioIsActive() { return s_pio_state.active; } + +void Rp1PioInitOrDie(const HardwareMapping &mapping, int double_rows, int parallel, + int pwm_lsb_nanoseconds, int dither_bits, + int row_address_type) { + Rp1PioState &state = s_pio_state; + if (state.active) return; + + state.row_address_type = row_address_type; + state.double_rows = double_rows; + state.output_enable_bit = mapping.output_enable; + state.latch_bit = mapping.strobe; + state.used_mask = BuildUsedMask(mapping, state.double_rows, parallel, + row_address_type); + + if (!FitsInPioWord(state.used_mask)) { + fprintf(stderr, + "The selected GPIO mapping uses pins outside RP1 PIO's 0..27 " + "range.\n"); + abort(); + } + if (!FitsInPioWord(mapping.clock) || !FitsInPioWord(mapping.strobe) + || !FitsInPioWord(mapping.output_enable) + || !FitsInPioWord(CollectAddressMask(mapping, state.double_rows, + row_address_type)) + || !FitsInPioWord(CollectColorMask(mapping, parallel))) { + fprintf(stderr, "The Pi 5-family RP1 backend only supports GPIO bits 0..27.\n"); + abort(); + } + if (!IsSingleBit(mapping.clock)) { + fprintf(stderr, + "The Pi 5-family RP1 PIO backend requires the clock signal to map to " + "exactly one GPIO pin.\n"); + abort(); + } + + PrepareBitplaneActiveWords(pwm_lsb_nanoseconds, dither_bits, + &state.bitplane_active_words); + ConfigureStateMachineOrDie(mapping); + state.active = true; +} + +void Rp1PioInitializePanels(const HardwareMapping &mapping, + const char *panel_type, int columns) { + if (!s_pio_state.active || panel_type == NULL || *panel_type == '\0') return; + + if (strncasecmp(panel_type, "fm6126", 6) == 0) { + SendFM6126Init(mapping, columns); + } else if (strncasecmp(panel_type, "fm6127", 6) == 0) { + SendFM6127Init(mapping, columns); + } else if (!s_pio_state.panel_init_warned) { + fprintf(stderr, + "Pi 5-family RP1 backend: panel init '%s' is not implemented; " + "continuing without panel init.\n", + panel_type); + s_pio_state.panel_init_warned = true; + } +} + +void Rp1PioDumpFramebuffer(Framebuffer *framebuffer, int pwm_low_bit) { + if (!s_pio_state.active || framebuffer == NULL) return; + + Rp1PioState &state = s_pio_state; + // The framebuffer already contains per-column color GPIO bits. This pass only + // adds row-address selection, latch/OE sequencing, and the active-time delay + // that gives each PWM bitplane its brightness weight. + const HardwareMapping &h = framebuffer->hardware_mapping(); + const int start_bit = + std::max(pwm_low_bit, Framebuffer::kBitPlanes - framebuffer->pwmbits()); + const int double_rows = framebuffer->double_rows(); + const int columns = framebuffer->columns(); + const int scan_mode = framebuffer->scan_mode(); + const bool uses_shift_register = + Rp1UsesShiftRegisterRowAddress(state.row_address_type); + const bool shifts_every_bitplane = + Rp1ShiftsRowAddressEveryBitplane(state.row_address_type); + + const int plane_count = Framebuffer::kBitPlanes - start_bit; + state.transfer_buffer.clear(); + state.transfer_buffer.reserve( + double_rows * plane_count * (columns + 8) + 8); + + uint32_t previous_addr = CalcRowAddressBits( + h, state.row_address_type, + DisplayRowFromLoop(double_rows - 1, double_rows, scan_mode)); + int previous_active_words = 0; + int last_shift_register_row = -1; + + for (int row_loop = 0; row_loop < double_rows; ++row_loop) { + const int display_row = + DisplayRowFromLoop(row_loop, double_rows, scan_mode); + const uint32_t current_addr = + CalcRowAddressBits(h, state.row_address_type, display_row); + + for (int bit = start_bit; bit < Framebuffer::kBitPlanes; ++bit) { + const gpio_bits_t *row_data = framebuffer->RowDataAt(display_row, bit); + AppendDataHeader(&state.transfer_buffer, columns); + + int remaining_overlap_words = previous_active_words; + for (int col = 0; col < columns; ++col) { + uint32_t pins = static_cast(row_data[col]) | previous_addr; + if (remaining_overlap_words <= 0) { + pins |= state.output_enable_bit; + } + state.transfer_buffer.push_back(pins); + if (remaining_overlap_words > 0) --remaining_overlap_words; + } + + if (remaining_overlap_words > 0) { + AppendDelay(&state.transfer_buffer, previous_addr, + remaining_overlap_words * kClocksPerDataWord); + } + + if (uses_shift_register) { + if (shifts_every_bitplane || display_row != last_shift_register_row) { + Rp1PioAppendRowAddress(&state.transfer_buffer, h, double_rows, + state.row_address_type, display_row, + state.output_enable_bit); + last_shift_register_row = display_row; + } + } else if (current_addr != previous_addr) { + AppendDelay(&state.transfer_buffer, + current_addr | state.output_enable_bit, + kPostAddressDelayClocks); + } + AppendDelay(&state.transfer_buffer, + current_addr | state.output_enable_bit | state.latch_bit, 0); + + previous_addr = current_addr; + previous_active_words = state.bitplane_active_words[bit]; + } + } + + if (previous_active_words > 0) { + AppendDelay(&state.transfer_buffer, previous_addr, + previous_active_words * kClocksPerDataWord); + } + AppendDelay(&state.transfer_buffer, previous_addr | state.output_enable_bit, + 0); + + const int rc = TransferLarge(state.pio, state.sm, &state.transfer_buffer[0], + state.transfer_buffer.size() + * sizeof(state.transfer_buffer[0])); + if (rc != 0) { + fprintf(stderr, "RP1 PIO framebuffer transfer failed: %d\n", rc); + abort(); + } +} + +void Rp1PioDeinit() { + Rp1PioState &state = s_pio_state; + if (!state.active) return; + + pio_sm_set_enabled(state.pio, state.sm, false); + pio_sm_clear_fifos(state.pio, state.sm); + ReleasePinDirection(state.pio, state.sm, state.used_mask); + pio_sm_unclaim(state.pio, state.sm); + pio_close(state.pio); + + state.active = false; + state.panel_init_warned = false; + state.pio = NULL; + state.sm = -1; + state.row_address_type = 0; + state.double_rows = 0; + state.gpio_slowdown = 1; + state.output_enable_bit = 0; + state.latch_bit = 0; + state.used_mask = 0; + state.bitplane_active_words.clear(); + state.transfer_buffer.clear(); +} + +} // namespace internal +} // namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_backend.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_backend.h new file mode 100644 index 000000000000..e1346c604ad1 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_backend.h @@ -0,0 +1,38 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +#ifndef RP1_PIO_BACKEND_H +#define RP1_PIO_BACKEND_H + +struct HardwareMapping; + +namespace rgb_matrix { +namespace internal { +class Framebuffer; + +// Internal Pi 5-family RP1 PIO renderer. +// +// Expected call flow: +// - detect/select with PlatformDetected()/ConfigSupported()/ShouldActivate() +// - push runtime timing with SetGpioSlowdown() +// - initialize once with InitOrDie() +// - optionally run panel-specific startup via InitializePanels() +// - stream each refresh via DumpFramebuffer() +// - release claimed state-machine resources with Deinit() +bool Rp1PioPlatformDetected(); +bool Rp1PioConfigSupported(const char *hardware_mapping, int row_address_type, + int parallel); +bool Rp1PioShouldActivate(const char *hardware_mapping, int row_address_type, + int parallel); +void Rp1PioSetGpioSlowdown(int slowdown); +bool Rp1PioIsActive(); +void Rp1PioInitOrDie(const HardwareMapping &mapping, int double_rows, int parallel, + int pwm_lsb_nanoseconds, int dither_bits, + int row_address_type); +void Rp1PioInitializePanels(const HardwareMapping &mapping, + const char *panel_type, int columns); +void Rp1PioDumpFramebuffer(Framebuffer *framebuffer, int pwm_low_bit); +void Rp1PioDeinit(); + +} // namespace internal +} // namespace rgb_matrix + +#endif // RP1_PIO_BACKEND_H diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_support.c b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_support.c new file mode 100644 index 000000000000..d32e8df2426e --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_support.c @@ -0,0 +1,9 @@ +/* + * Pull the small vendor piolib implementation into one local translation unit. + * + * The project build files only need to know about rp1_pio_support.c; this + * wrapper keeps the vendored RP1 C sources grouped together for both Make and + * CMake and avoids sprinkling vendor-specific source lists through the tree. + */ +#include "rp1_pio_vendor/piolib/pio_rp1.c" +#include "rp1_pio_vendor/piolib/piolib.c" diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/include/piomatter/protomatter.pio.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/include/piomatter/protomatter.pio.h new file mode 100644 index 000000000000..02522f271393 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/include/piomatter/protomatter.pio.h @@ -0,0 +1,53 @@ +#pragma once + +const int protomatter_wrap = 4; +const int protomatter_wrap_target = 0; +const int protomatter_sideset_pin_count = 1; +const bool protomatter_sideset_enable = 1; +const uint16_t protomatter[] = { + // ; data format (out-shift-right): + // ; MSB ... LSB + // ; 0 ddd......ddd: 31-bit delay + // ; 1 ccc......ccc: 31 bit data count + // .side_set 1 opt + // .wrap_target + // top: + 0x6021, // out x, 1 + 0x605f, // out y, 31 + 0x0025, // jmp !x do_delay + // data_loop: + 0x6000, // out pins, 32 + 0x1883, // jmp y--, data_loop side 1 ; assert clk bit + // .wrap + // do_delay: + 0x6000, // out pins, 32 + // delay_loop: + 0x0086, // jmp y--, delay_loop + 0x0000, // jmp top + // ;; fill program out to 32 instructions so nothing else can load + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop + 0xa042, // nop +}; diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/clocks.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/clocks.h new file mode 100644 index 000000000000..aa3e766f832a --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/clocks.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Raspberry Pi Ltd. + * All rights reserved. + */ + +#ifndef _HARDWARE_CLOCKS_H +#define _HARDWARE_CLOCKS_H + +enum clock_index { + clk_gpout0 = 0, ///< GPIO Muxing 0 + clk_gpout1, ///< GPIO Muxing 1 + clk_gpout2, ///< GPIO Muxing 2 + clk_gpout3, ///< GPIO Muxing 3 + clk_ref, ///< Watchdog and timers reference clock + clk_sys, ///< Processors, bus fabric, memory, memory mapped registers + clk_peri, ///< Peripheral clock for UART and SPI + clk_usb, ///< USB clock + clk_adc, ///< ADC clock + clk_rtc, ///< Real time clock + CLK_COUNT +}; + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/gpio.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/gpio.h new file mode 100644 index 000000000000..cf6a2da7bf1c --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/gpio.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Raspberry Pi Ltd. + * All rights reserved. + */ +#ifndef _HARDWARE_GPIO_H +#define _HARDWARE_GPIO_H + +#include "pio_platform.h" + +#ifndef PARAM_ASSERTIONS_ENABLED_GPIO +#define PARAM_ASSERTIONS_ENABLED_GPIO 0 +#endif + +#define NUM_BANK0_GPIOS 32 + +enum gpio_function { + GPIO_FUNC_XIP = 0, + GPIO_FUNC_SPI = 1, + GPIO_FUNC_UART = 2, + GPIO_FUNC_I2C = 3, + GPIO_FUNC_PWM = 4, + GPIO_FUNC_SIO = 5, + GPIO_FUNC_PIO0 = 6, + GPIO_FUNC_PIO1 = 7, + GPIO_FUNC_GPCK = 8, + GPIO_FUNC_USB = 9, + GPIO_FUNC_NULL = 0x1f, +}; + +#define GPIO_OUT 1 +#define GPIO_IN 0 + +enum gpio_irq_level { + GPIO_IRQ_LEVEL_LOW = 0x1u, + GPIO_IRQ_LEVEL_HIGH = 0x2u, + GPIO_IRQ_EDGE_FALL = 0x4u, + GPIO_IRQ_EDGE_RISE = 0x8u, +}; + +enum gpio_override { + GPIO_OVERRIDE_NORMAL = 0, ///< peripheral signal selected via \ref gpio_set_function + GPIO_OVERRIDE_INVERT = 1, ///< invert peripheral signal selected via \ref gpio_set_function + GPIO_OVERRIDE_LOW = 2, ///< drive low/disable output + GPIO_OVERRIDE_HIGH = 3, ///< drive high/enable output +}; +enum gpio_slew_rate { + GPIO_SLEW_RATE_SLOW = 0, ///< Slew rate limiting enabled + GPIO_SLEW_RATE_FAST = 1 ///< Slew rate limiting disabled +}; + +enum gpio_drive_strength { + GPIO_DRIVE_STRENGTH_2MA = 0, ///< 2 mA nominal drive strength + GPIO_DRIVE_STRENGTH_4MA = 1, ///< 4 mA nominal drive strength + GPIO_DRIVE_STRENGTH_8MA = 2, ///< 8 mA nominal drive strength + GPIO_DRIVE_STRENGTH_12MA = 3 ///< 12 mA nominal drive strength +}; + +static inline void check_gpio_param(__unused uint gpio) { + invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS); +} + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/pio.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/pio.h new file mode 100644 index 000000000000..38a6212426ae --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/pio.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Raspberry Pi Ltd. + * All rights reserved. + */ +#ifndef _HARDWARE_PIO_H +#define _HARDWARE_PIO_H + +#include "piolib.h" + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/pio_instructions.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/pio_instructions.h new file mode 100644 index 000000000000..d993d74e0060 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/pio_instructions.h @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _HARDWARE_PIO_INSTRUCTIONS_H +#define _HARDWARE_PIO_INSTRUCTIONS_H + +//#include "pico.h" + +/** \brief PIO instruction encoding + * \defgroup pio_instructions pio_instructions + * \ingroup hardware_pio + * + * Functions for generating PIO instruction encodings programmatically. In debug builds + *`PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` can be set to 1 to enable validation of encoding function + * parameters. + * + * For fuller descriptions of the instructions in question see the "RP2040 Datasheet" + */ + +// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS, Enable/disable assertions in the PIO instructions, type=bool, default=0, group=pio_instructions +#ifndef PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS +#define PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum pio_instr_bits { + pio_instr_bits_jmp = 0x0000, + pio_instr_bits_wait = 0x2000, + pio_instr_bits_in = 0x4000, + pio_instr_bits_out = 0x6000, + pio_instr_bits_push = 0x8000, + pio_instr_bits_pull = 0x8080, + pio_instr_bits_mov = 0xa000, + pio_instr_bits_irq = 0xc000, + pio_instr_bits_set = 0xe000, +}; + +#ifndef NDEBUG +#define _PIO_INVALID_IN_SRC 0x08u +#define _PIO_INVALID_OUT_DEST 0x10u +#define _PIO_INVALID_SET_DEST 0x20u +#define _PIO_INVALID_MOV_SRC 0x40u +#define _PIO_INVALID_MOV_DEST 0x80u +#else +#define _PIO_INVALID_IN_SRC 0u +#define _PIO_INVALID_OUT_DEST 0u +#define _PIO_INVALID_SET_DEST 0u +#define _PIO_INVALID_MOV_SRC 0u +#define _PIO_INVALID_MOV_DEST 0u +#endif + +/*! \brief Enumeration of values to pass for source/destination args for instruction encoding functions + * \ingroup pio_instructions + * + * \note Not all values are suitable for all functions. Validity is only checked in debug mode when + * `PARAM_ASSERTIONS_ENABLED_PIO_INSTRUCTIONS` is 1 + */ +enum pio_src_dest { + pio_pins = 0u, + pio_x = 1u, + pio_y = 2u, + pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, + pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, + pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, + pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, + pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, + pio_isr = 6u | _PIO_INVALID_SET_DEST, + pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST, + pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, +}; + +static inline uint _pio_major_instr_bits(uint instr) { + return instr & 0xe000u; +} + +static inline uint _pio_encode_instr_and_args(enum pio_instr_bits instr_bits, uint arg1, uint arg2) { + valid_params_if(PIO_INSTRUCTIONS, arg1 <= 0x7); +#if PARAM_ASSERTIONS_ENABLED(PIO_INSTRUCTIONS) + uint32_t major = _pio_major_instr_bits(instr_bits); + if (major == pio_instr_bits_in || major == pio_instr_bits_out) { + assert(arg2 && arg2 <= 32); + } else { + assert(arg2 <= 31); + } +#endif + return instr_bits | (arg1 << 5u) | (arg2 & 0x1fu); +} + +static inline uint _pio_encode_instr_and_src_dest(enum pio_instr_bits instr_bits, enum pio_src_dest dest, uint value) { + return _pio_encode_instr_and_args(instr_bits, dest & 7u, value); +} + +/*! \brief Encode just the delay slot bits of an instruction + * \ingroup pio_instructions + * + * \note This function does not return a valid instruction encoding; instead it returns an encoding of the delay + * slot suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when + * combining the results of this function with the results of \ref pio_encode_sideset and \ref pio_encode_sideset_opt + * as they share the same bits within the instruction encoding. + * + * \param cycles the number of cycles 0-31 (or less if side set is being used) + * \return the delay slot bits to be ORed with an instruction encoding + */ +static inline uint pio_encode_delay(uint cycles) { + // note that the maximum cycles will be smaller if sideset_bit_count > 0 + valid_params_if(PIO_INSTRUCTIONS, cycles <= 0x1f); + return cycles << 8u; +} + +/*! \brief Encode just the side set bits of an instruction (in non optional side set mode) + * \ingroup pio_instructions + * + * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits + * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when + * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits + * within the instruction encoding. + * + * \param sideset_bit_count number of side set bits as would be specified via `.sideset` in pioasm + * \param value the value to sideset on the pins + * \return the side set bits to be ORed with an instruction encoding + */ +static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) { + valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 5); + valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); + return value << (13u - sideset_bit_count); +} + +/*! \brief Encode just the side set bits of an instruction (in optional -`opt` side set mode) + * \ingroup pio_instructions + * + * \note This function does not return a valid instruction encoding; instead it returns an encoding of the side set bits + * suitable for `OR`ing with the result of an encoding function for an actual instruction. Care should be taken when + * combining the results of this function with the results of \ref pio_encode_delay as they share the same bits + * within the instruction encoding. + * + * \param sideset_bit_count number of side set bits as would be specified via `.sideset opt` in pioasm + * \param value the value to sideset on the pins + * \return the side set bits to be ORed with an instruction encoding + */ +static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) { + valid_params_if(PIO_INSTRUCTIONS, sideset_bit_count >= 1 && sideset_bit_count <= 4); + valid_params_if(PIO_INSTRUCTIONS, value <= ((1u << sideset_bit_count) - 1)); + return 0x1000u | value << (12u - sideset_bit_count); +} + +/*! \brief Encode an unconditional JMP instruction + * \ingroup pio_instructions + * + * This is the equivalent of `JMP ` + * + * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_jmp(uint addr) { + return _pio_encode_instr_and_args(pio_instr_bits_jmp, 0, addr); +} + +/*! \brief Encode a conditional JMP if scratch X zero instruction + * \ingroup pio_instructions + * + * This is the equivalent of `JMP !X ` + * + * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_jmp_not_x(uint addr) { + return _pio_encode_instr_and_args(pio_instr_bits_jmp, 1, addr); +} + +/*! \brief Encode a conditional JMP if scratch X non-zero (and post-decrement X) instruction + * \ingroup pio_instructions + * + * This is the equivalent of `JMP X-- ` + * + * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_jmp_x_dec(uint addr) { + return _pio_encode_instr_and_args(pio_instr_bits_jmp, 2, addr); +} + +/*! \brief Encode a conditional JMP if scratch Y zero instruction + * \ingroup pio_instructions + * + * This is the equivalent of `JMP !Y ` + * + * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_jmp_not_y(uint addr) { + return _pio_encode_instr_and_args(pio_instr_bits_jmp, 3, addr); +} + +/*! \brief Encode a conditional JMP if scratch Y non-zero (and post-decrement Y) instruction + * \ingroup pio_instructions + * + * This is the equivalent of `JMP Y-- ` + * + * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_jmp_y_dec(uint addr) { + return _pio_encode_instr_and_args(pio_instr_bits_jmp, 4, addr); +} + +/*! \brief Encode a conditional JMP if scratch X not equal scratch Y instruction + * \ingroup pio_instructions + * + * This is the equivalent of `JMP X!=Y ` + * + * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_jmp_x_ne_y(uint addr) { + return _pio_encode_instr_and_args(pio_instr_bits_jmp, 5, addr); +} + +/*! \brief Encode a conditional JMP if input pin high instruction + * \ingroup pio_instructions + * + * This is the equivalent of `JMP PIN ` + * + * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_jmp_pin(uint addr) { + return _pio_encode_instr_and_args(pio_instr_bits_jmp, 6, addr); +} + +/*! \brief Encode a conditional JMP if output shift register not empty instruction + * \ingroup pio_instructions + * + * This is the equivalent of `JMP !OSRE ` + * + * \param addr The target address 0-31 (an absolute address within the PIO instruction memory) + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_jmp_not_osre(uint addr) { + return _pio_encode_instr_and_args(pio_instr_bits_jmp, 7, addr); +} + +static inline uint _pio_encode_irq(bool relative, uint irq) { + valid_params_if(PIO_INSTRUCTIONS, irq <= 7); + return (relative ? 0x10u : 0x0u) | irq; +} + +/*! \brief Encode a WAIT for GPIO pin instruction + * \ingroup pio_instructions + * + * This is the equivalent of `WAIT GPIO ` + * + * \param polarity true for `WAIT 1`, false for `WAIT 0` + * \param gpio The real GPIO number 0-31 + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) { + return _pio_encode_instr_and_args(pio_instr_bits_wait, 0u | (polarity ? 4u : 0u), gpio); +} + +/*! \brief Encode a WAIT for pin instruction + * \ingroup pio_instructions + * + * This is the equivalent of `WAIT PIN ` + * + * \param polarity true for `WAIT 1`, false for `WAIT 0` + * \param pin The pin number 0-31 relative to the executing SM's input pin mapping + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_wait_pin(bool polarity, uint pin) { + return _pio_encode_instr_and_args(pio_instr_bits_wait, 1u | (polarity ? 4u : 0u), pin); +} + +/*! \brief Encode a WAIT for IRQ instruction + * \ingroup pio_instructions + * + * This is the equivalent of `WAIT IRQ ` + * + * \param polarity true for `WAIT 1`, false for `WAIT 0` + * \param relative true for a `WAIT IRQ REL`, false for regular `WAIT IRQ ` + * \param irq the irq number 0-7 + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) { + valid_params_if(PIO_INSTRUCTIONS, irq <= 7); + return _pio_encode_instr_and_args(pio_instr_bits_wait, 2u | (polarity ? 4u : 0u), _pio_encode_irq(relative, irq)); +} + +/*! \brief Encode an IN instruction + * \ingroup pio_instructions + * + * This is the equivalent of `IN , ` + * + * \param src The source to take data from + * \param count The number of bits 1-32 + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_in(enum pio_src_dest src, uint count) { + valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_IN_SRC)); + return _pio_encode_instr_and_src_dest(pio_instr_bits_in, src, count); +} + +/*! \brief Encode an OUT instruction + * \ingroup pio_instructions + * + * This is the equivalent of `OUT , ` + * + * \param dest The destination to write data to + * \param count The number of bits 1-32 + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_out(enum pio_src_dest dest, uint count) { + valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_OUT_DEST)); + return _pio_encode_instr_and_src_dest(pio_instr_bits_out, dest, count); +} + +/*! \brief Encode a PUSH instruction + * \ingroup pio_instructions + * + * This is the equivalent of `PUSH , ` + * + * \param if_full true for `PUSH IF_FULL ...`, false for `PUSH ...` + * \param block true for `PUSH ... BLOCK`, false for `PUSH ...` + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_push(bool if_full, bool block) { + return _pio_encode_instr_and_args(pio_instr_bits_push, (if_full ? 2u : 0u) | (block ? 1u : 0u), 0); +} + +/*! \brief Encode a PULL instruction + * \ingroup pio_instructions + * + * This is the equivalent of `PULL , ` + * + * \param if_empty true for `PULL IF_EMPTY ...`, false for `PULL ...` + * \param block true for `PULL ... BLOCK`, false for `PULL ...` + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_pull(bool if_empty, bool block) { + return _pio_encode_instr_and_args(pio_instr_bits_pull, (if_empty ? 2u : 0u) | (block ? 1u : 0u), 0); +} + +/*! \brief Encode a MOV instruction + * \ingroup pio_instructions + * + * This is the equivalent of `MOV , ` + * + * \param dest The destination to write data to + * \param src The source to take data from + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) { + valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); + valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); + return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, src & 7u); +} + +/*! \brief Encode a MOV instruction with bit invert + * \ingroup pio_instructions + * + * This is the equivalent of `MOV , ~` + * + * \param dest The destination to write inverted data to + * \param src The source to take data from + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) { + valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); + valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); + return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (1u << 3u) | (src & 7u)); +} + +/*! \brief Encode a MOV instruction with bit reverse + * \ingroup pio_instructions + * + * This is the equivalent of `MOV , ::` + * + * \param dest The destination to write bit reversed data to + * \param src The source to take data from + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) { + valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_MOV_DEST)); + valid_params_if(PIO_INSTRUCTIONS, !(src & _PIO_INVALID_MOV_SRC)); + return _pio_encode_instr_and_src_dest(pio_instr_bits_mov, dest, (2u << 3u) | (src & 7u)); +} + +/*! \brief Encode a IRQ SET instruction + * \ingroup pio_instructions + * + * This is the equivalent of `IRQ SET ` + * + * \param relative true for a `IRQ SET REL`, false for regular `IRQ SET ` + * \param irq the irq number 0-7 + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_irq_set(bool relative, uint irq) { + return _pio_encode_instr_and_args(pio_instr_bits_irq, 0, _pio_encode_irq(relative, irq)); +} + +/*! \brief Encode a IRQ WAIT instruction + * \ingroup pio_instructions + * + * This is the equivalent of `IRQ WAIT ` + * + * \param relative true for a `IRQ WAIT REL`, false for regular `IRQ WAIT ` + * \param irq the irq number 0-7 + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_irq_wait(bool relative, uint irq) { + return _pio_encode_instr_and_args(pio_instr_bits_irq, 1, _pio_encode_irq(relative, irq)); +} + +/*! \brief Encode a IRQ CLEAR instruction + * \ingroup pio_instructions + * + * This is the equivalent of `IRQ CLEAR ` + * + * \param relative true for a `IRQ CLEAR REL`, false for regular `IRQ CLEAR ` + * \param irq the irq number 0-7 + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_irq_clear(bool relative, uint irq) { + return _pio_encode_instr_and_args(pio_instr_bits_irq, 2, _pio_encode_irq(relative, irq)); +} + +/*! \brief Encode a SET instruction + * \ingroup pio_instructions + * + * This is the equivalent of `SET , ` + * + * \param dest The destination to apply the value to + * \param value The value 0-31 + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_set(enum pio_src_dest dest, uint value) { + valid_params_if(PIO_INSTRUCTIONS, !(dest & _PIO_INVALID_SET_DEST)); + return _pio_encode_instr_and_src_dest(pio_instr_bits_set, dest, value); +} + +/*! \brief Encode a NOP instruction + * \ingroup pio_instructions + * + * This is the equivalent of `NOP` which is itself encoded as `MOV y, y` + * + * \return The instruction encoding with 0 delay and no side set value + * \see pio_encode_delay, pio_encode_sideset, pio_encode_sideset_opt + */ +static inline uint pio_encode_nop(void) { + return pio_encode_mov(pio_y, pio_y); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/regs/proc_pio.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/regs/proc_pio.h new file mode 100644 index 000000000000..4bf9db1781fd --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/hardware/regs/proc_pio.h @@ -0,0 +1,3222 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +// ============================================================================= +// Register block : PROC_PIO +// Version : 1 +// Bus type : ahbl +// Description : Programmable IO block +// ============================================================================= +#ifndef HARDWARE_REGS_PROC_PIO_DEFINED +#define HARDWARE_REGS_PROC_PIO_DEFINED +// ============================================================================= +// Register : PROC_PIO_CTRL +// Description : PIO control register +#define PROC_PIO_CTRL_OFFSET _u(0x00000000) +#define PROC_PIO_CTRL_BITS _u(0x00000fff) +#define PROC_PIO_CTRL_RESET _u(0x00000000) +#define PROC_PIO_CTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_CTRL_CLKDIV_RESTART +// Description : Force clock dividers to restart their count and clear +// fractional +// accumulators. Restart multiple dividers to synchronise them. +#define PROC_PIO_CTRL_CLKDIV_RESTART_RESET _u(0x0) +#define PROC_PIO_CTRL_CLKDIV_RESTART_BITS _u(0x00000f00) +#define PROC_PIO_CTRL_CLKDIV_RESTART_MSB _u(11) +#define PROC_PIO_CTRL_CLKDIV_RESTART_LSB _u(8) +#define PROC_PIO_CTRL_CLKDIV_RESTART_ACCESS "SC" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_CTRL_SM_RESTART +// Description : Clear internal SM state which is otherwise difficult to access +// (e.g. shift counters). Self-clearing. +#define PROC_PIO_CTRL_SM_RESTART_RESET _u(0x0) +#define PROC_PIO_CTRL_SM_RESTART_BITS _u(0x000000f0) +#define PROC_PIO_CTRL_SM_RESTART_MSB _u(7) +#define PROC_PIO_CTRL_SM_RESTART_LSB _u(4) +#define PROC_PIO_CTRL_SM_RESTART_ACCESS "SC" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_CTRL_SM_ENABLE +// Description : Enable state machine +#define PROC_PIO_CTRL_SM_ENABLE_RESET _u(0x0) +#define PROC_PIO_CTRL_SM_ENABLE_BITS _u(0x0000000f) +#define PROC_PIO_CTRL_SM_ENABLE_MSB _u(3) +#define PROC_PIO_CTRL_SM_ENABLE_LSB _u(0) +#define PROC_PIO_CTRL_SM_ENABLE_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_FSTAT +// Description : FIFO status register +#define PROC_PIO_FSTAT_OFFSET _u(0x00000004) +#define PROC_PIO_FSTAT_BITS _u(0x0f0f0f0f) +#define PROC_PIO_FSTAT_RESET _u(0x0f000f00) +#define PROC_PIO_FSTAT_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FSTAT_TXEMPTY +// Description : State machine TX FIFO is empty +#define PROC_PIO_FSTAT_TXEMPTY_RESET _u(0xf) +#define PROC_PIO_FSTAT_TXEMPTY_BITS _u(0x0f000000) +#define PROC_PIO_FSTAT_TXEMPTY_MSB _u(27) +#define PROC_PIO_FSTAT_TXEMPTY_LSB _u(24) +#define PROC_PIO_FSTAT_TXEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FSTAT_TXFULL +// Description : State machine TX FIFO is full +#define PROC_PIO_FSTAT_TXFULL_RESET _u(0x0) +#define PROC_PIO_FSTAT_TXFULL_BITS _u(0x000f0000) +#define PROC_PIO_FSTAT_TXFULL_MSB _u(19) +#define PROC_PIO_FSTAT_TXFULL_LSB _u(16) +#define PROC_PIO_FSTAT_TXFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FSTAT_RXEMPTY +// Description : State machine RX FIFO is empty +#define PROC_PIO_FSTAT_RXEMPTY_RESET _u(0xf) +#define PROC_PIO_FSTAT_RXEMPTY_BITS _u(0x00000f00) +#define PROC_PIO_FSTAT_RXEMPTY_MSB _u(11) +#define PROC_PIO_FSTAT_RXEMPTY_LSB _u(8) +#define PROC_PIO_FSTAT_RXEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FSTAT_RXFULL +// Description : State machine RX FIFO is full +#define PROC_PIO_FSTAT_RXFULL_RESET _u(0x0) +#define PROC_PIO_FSTAT_RXFULL_BITS _u(0x0000000f) +#define PROC_PIO_FSTAT_RXFULL_MSB _u(3) +#define PROC_PIO_FSTAT_RXFULL_LSB _u(0) +#define PROC_PIO_FSTAT_RXFULL_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_FDEBUG +// Description : FIFO debug register +#define PROC_PIO_FDEBUG_OFFSET _u(0x00000008) +#define PROC_PIO_FDEBUG_BITS _u(0x0f0f0f0f) +#define PROC_PIO_FDEBUG_RESET _u(0x00000000) +#define PROC_PIO_FDEBUG_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FDEBUG_TXSTALL +// Description : State machine has stalled on empty TX FIFO. Write 1 to clear. +#define PROC_PIO_FDEBUG_TXSTALL_RESET _u(0x0) +#define PROC_PIO_FDEBUG_TXSTALL_BITS _u(0x0f000000) +#define PROC_PIO_FDEBUG_TXSTALL_MSB _u(27) +#define PROC_PIO_FDEBUG_TXSTALL_LSB _u(24) +#define PROC_PIO_FDEBUG_TXSTALL_ACCESS "WC" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FDEBUG_TXOVER +// Description : TX FIFO overflow has occurred. Write 1 to clear. +#define PROC_PIO_FDEBUG_TXOVER_RESET _u(0x0) +#define PROC_PIO_FDEBUG_TXOVER_BITS _u(0x000f0000) +#define PROC_PIO_FDEBUG_TXOVER_MSB _u(19) +#define PROC_PIO_FDEBUG_TXOVER_LSB _u(16) +#define PROC_PIO_FDEBUG_TXOVER_ACCESS "WC" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FDEBUG_RXUNDER +// Description : RX FIFO underflow has occurred. Write 1 to clear. +#define PROC_PIO_FDEBUG_RXUNDER_RESET _u(0x0) +#define PROC_PIO_FDEBUG_RXUNDER_BITS _u(0x00000f00) +#define PROC_PIO_FDEBUG_RXUNDER_MSB _u(11) +#define PROC_PIO_FDEBUG_RXUNDER_LSB _u(8) +#define PROC_PIO_FDEBUG_RXUNDER_ACCESS "WC" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FDEBUG_RXSTALL +// Description : State machine has stalled on full RX FIFO. Write 1 to clear. +#define PROC_PIO_FDEBUG_RXSTALL_RESET _u(0x0) +#define PROC_PIO_FDEBUG_RXSTALL_BITS _u(0x0000000f) +#define PROC_PIO_FDEBUG_RXSTALL_MSB _u(3) +#define PROC_PIO_FDEBUG_RXSTALL_LSB _u(0) +#define PROC_PIO_FDEBUG_RXSTALL_ACCESS "WC" +// ============================================================================= +// Register : PROC_PIO_FLEVEL +// Description : FIFO levels +// These count up to 15 only. When counting higher the extra bit +// is set in flevel2 register and this value saturates +#define PROC_PIO_FLEVEL_OFFSET _u(0x0000000c) +#define PROC_PIO_FLEVEL_BITS _u(0xffffffff) +#define PROC_PIO_FLEVEL_RESET _u(0x00000000) +#define PROC_PIO_FLEVEL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL_RX3 +// Description : None +#define PROC_PIO_FLEVEL_RX3_RESET _u(0x0) +#define PROC_PIO_FLEVEL_RX3_BITS _u(0xf0000000) +#define PROC_PIO_FLEVEL_RX3_MSB _u(31) +#define PROC_PIO_FLEVEL_RX3_LSB _u(28) +#define PROC_PIO_FLEVEL_RX3_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL_TX3 +// Description : None +#define PROC_PIO_FLEVEL_TX3_RESET _u(0x0) +#define PROC_PIO_FLEVEL_TX3_BITS _u(0x0f000000) +#define PROC_PIO_FLEVEL_TX3_MSB _u(27) +#define PROC_PIO_FLEVEL_TX3_LSB _u(24) +#define PROC_PIO_FLEVEL_TX3_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL_RX2 +// Description : None +#define PROC_PIO_FLEVEL_RX2_RESET _u(0x0) +#define PROC_PIO_FLEVEL_RX2_BITS _u(0x00f00000) +#define PROC_PIO_FLEVEL_RX2_MSB _u(23) +#define PROC_PIO_FLEVEL_RX2_LSB _u(20) +#define PROC_PIO_FLEVEL_RX2_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL_TX2 +// Description : None +#define PROC_PIO_FLEVEL_TX2_RESET _u(0x0) +#define PROC_PIO_FLEVEL_TX2_BITS _u(0x000f0000) +#define PROC_PIO_FLEVEL_TX2_MSB _u(19) +#define PROC_PIO_FLEVEL_TX2_LSB _u(16) +#define PROC_PIO_FLEVEL_TX2_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL_RX1 +// Description : None +#define PROC_PIO_FLEVEL_RX1_RESET _u(0x0) +#define PROC_PIO_FLEVEL_RX1_BITS _u(0x0000f000) +#define PROC_PIO_FLEVEL_RX1_MSB _u(15) +#define PROC_PIO_FLEVEL_RX1_LSB _u(12) +#define PROC_PIO_FLEVEL_RX1_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL_TX1 +// Description : None +#define PROC_PIO_FLEVEL_TX1_RESET _u(0x0) +#define PROC_PIO_FLEVEL_TX1_BITS _u(0x00000f00) +#define PROC_PIO_FLEVEL_TX1_MSB _u(11) +#define PROC_PIO_FLEVEL_TX1_LSB _u(8) +#define PROC_PIO_FLEVEL_TX1_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL_RX0 +// Description : None +#define PROC_PIO_FLEVEL_RX0_RESET _u(0x0) +#define PROC_PIO_FLEVEL_RX0_BITS _u(0x000000f0) +#define PROC_PIO_FLEVEL_RX0_MSB _u(7) +#define PROC_PIO_FLEVEL_RX0_LSB _u(4) +#define PROC_PIO_FLEVEL_RX0_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL_TX0 +// Description : None +#define PROC_PIO_FLEVEL_TX0_RESET _u(0x0) +#define PROC_PIO_FLEVEL_TX0_BITS _u(0x0000000f) +#define PROC_PIO_FLEVEL_TX0_MSB _u(3) +#define PROC_PIO_FLEVEL_TX0_LSB _u(0) +#define PROC_PIO_FLEVEL_TX0_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_FLEVEL2 +// Description : FIFO level extra bits +// These are only used in double fifo mode, and the fifo has more +// than 15 elements +#define PROC_PIO_FLEVEL2_OFFSET _u(0x00000010) +#define PROC_PIO_FLEVEL2_BITS _u(0x11111111) +#define PROC_PIO_FLEVEL2_RESET _u(0x00000000) +#define PROC_PIO_FLEVEL2_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL2_RX3 +// Description : None +#define PROC_PIO_FLEVEL2_RX3_RESET _u(0x0) +#define PROC_PIO_FLEVEL2_RX3_BITS _u(0x10000000) +#define PROC_PIO_FLEVEL2_RX3_MSB _u(28) +#define PROC_PIO_FLEVEL2_RX3_LSB _u(28) +#define PROC_PIO_FLEVEL2_RX3_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL2_TX3 +// Description : None +#define PROC_PIO_FLEVEL2_TX3_RESET _u(0x0) +#define PROC_PIO_FLEVEL2_TX3_BITS _u(0x01000000) +#define PROC_PIO_FLEVEL2_TX3_MSB _u(24) +#define PROC_PIO_FLEVEL2_TX3_LSB _u(24) +#define PROC_PIO_FLEVEL2_TX3_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL2_RX2 +// Description : None +#define PROC_PIO_FLEVEL2_RX2_RESET _u(0x0) +#define PROC_PIO_FLEVEL2_RX2_BITS _u(0x00100000) +#define PROC_PIO_FLEVEL2_RX2_MSB _u(20) +#define PROC_PIO_FLEVEL2_RX2_LSB _u(20) +#define PROC_PIO_FLEVEL2_RX2_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL2_TX2 +// Description : None +#define PROC_PIO_FLEVEL2_TX2_RESET _u(0x0) +#define PROC_PIO_FLEVEL2_TX2_BITS _u(0x00010000) +#define PROC_PIO_FLEVEL2_TX2_MSB _u(16) +#define PROC_PIO_FLEVEL2_TX2_LSB _u(16) +#define PROC_PIO_FLEVEL2_TX2_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL2_RX1 +// Description : None +#define PROC_PIO_FLEVEL2_RX1_RESET _u(0x0) +#define PROC_PIO_FLEVEL2_RX1_BITS _u(0x00001000) +#define PROC_PIO_FLEVEL2_RX1_MSB _u(12) +#define PROC_PIO_FLEVEL2_RX1_LSB _u(12) +#define PROC_PIO_FLEVEL2_RX1_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL2_TX1 +// Description : None +#define PROC_PIO_FLEVEL2_TX1_RESET _u(0x0) +#define PROC_PIO_FLEVEL2_TX1_BITS _u(0x00000100) +#define PROC_PIO_FLEVEL2_TX1_MSB _u(8) +#define PROC_PIO_FLEVEL2_TX1_LSB _u(8) +#define PROC_PIO_FLEVEL2_TX1_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL2_RX0 +// Description : None +#define PROC_PIO_FLEVEL2_RX0_RESET _u(0x0) +#define PROC_PIO_FLEVEL2_RX0_BITS _u(0x00000010) +#define PROC_PIO_FLEVEL2_RX0_MSB _u(4) +#define PROC_PIO_FLEVEL2_RX0_LSB _u(4) +#define PROC_PIO_FLEVEL2_RX0_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_FLEVEL2_TX0 +// Description : None +#define PROC_PIO_FLEVEL2_TX0_RESET _u(0x0) +#define PROC_PIO_FLEVEL2_TX0_BITS _u(0x00000001) +#define PROC_PIO_FLEVEL2_TX0_MSB _u(0) +#define PROC_PIO_FLEVEL2_TX0_LSB _u(0) +#define PROC_PIO_FLEVEL2_TX0_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_TXF0 +// Description : Direct write access to the TX FIFO for this state machine. Each +// write pushes one word to the FIFO. +#define PROC_PIO_TXF0_OFFSET _u(0x00000014) +#define PROC_PIO_TXF0_BITS _u(0xffffffff) +#define PROC_PIO_TXF0_RESET _u(0x00000000) +#define PROC_PIO_TXF0_WIDTH _u(32) +#define PROC_PIO_TXF0_MSB _u(31) +#define PROC_PIO_TXF0_LSB _u(0) +#define PROC_PIO_TXF0_ACCESS "WF" +// ============================================================================= +// Register : PROC_PIO_TXF1 +// Description : Direct write access to the TX FIFO for this state machine. Each +// write pushes one word to the FIFO. +#define PROC_PIO_TXF1_OFFSET _u(0x00000018) +#define PROC_PIO_TXF1_BITS _u(0xffffffff) +#define PROC_PIO_TXF1_RESET _u(0x00000000) +#define PROC_PIO_TXF1_WIDTH _u(32) +#define PROC_PIO_TXF1_MSB _u(31) +#define PROC_PIO_TXF1_LSB _u(0) +#define PROC_PIO_TXF1_ACCESS "WF" +// ============================================================================= +// Register : PROC_PIO_TXF2 +// Description : Direct write access to the TX FIFO for this state machine. Each +// write pushes one word to the FIFO. +#define PROC_PIO_TXF2_OFFSET _u(0x0000001c) +#define PROC_PIO_TXF2_BITS _u(0xffffffff) +#define PROC_PIO_TXF2_RESET _u(0x00000000) +#define PROC_PIO_TXF2_WIDTH _u(32) +#define PROC_PIO_TXF2_MSB _u(31) +#define PROC_PIO_TXF2_LSB _u(0) +#define PROC_PIO_TXF2_ACCESS "WF" +// ============================================================================= +// Register : PROC_PIO_TXF3 +// Description : Direct write access to the TX FIFO for this state machine. Each +// write pushes one word to the FIFO. +#define PROC_PIO_TXF3_OFFSET _u(0x00000020) +#define PROC_PIO_TXF3_BITS _u(0xffffffff) +#define PROC_PIO_TXF3_RESET _u(0x00000000) +#define PROC_PIO_TXF3_WIDTH _u(32) +#define PROC_PIO_TXF3_MSB _u(31) +#define PROC_PIO_TXF3_LSB _u(0) +#define PROC_PIO_TXF3_ACCESS "WF" +// ============================================================================= +// Register : PROC_PIO_RXF0 +// Description : Direct read access to the RX FIFO for this state machine. Each +// read pops one word from the FIFO. +#define PROC_PIO_RXF0_OFFSET _u(0x00000024) +#define PROC_PIO_RXF0_BITS _u(0xffffffff) +#define PROC_PIO_RXF0_RESET "-" +#define PROC_PIO_RXF0_WIDTH _u(32) +#define PROC_PIO_RXF0_MSB _u(31) +#define PROC_PIO_RXF0_LSB _u(0) +#define PROC_PIO_RXF0_ACCESS "RF" +// ============================================================================= +// Register : PROC_PIO_RXF1 +// Description : Direct read access to the RX FIFO for this state machine. Each +// read pops one word from the FIFO. +#define PROC_PIO_RXF1_OFFSET _u(0x00000028) +#define PROC_PIO_RXF1_BITS _u(0xffffffff) +#define PROC_PIO_RXF1_RESET "-" +#define PROC_PIO_RXF1_WIDTH _u(32) +#define PROC_PIO_RXF1_MSB _u(31) +#define PROC_PIO_RXF1_LSB _u(0) +#define PROC_PIO_RXF1_ACCESS "RF" +// ============================================================================= +// Register : PROC_PIO_RXF2 +// Description : Direct read access to the RX FIFO for this state machine. Each +// read pops one word from the FIFO. +#define PROC_PIO_RXF2_OFFSET _u(0x0000002c) +#define PROC_PIO_RXF2_BITS _u(0xffffffff) +#define PROC_PIO_RXF2_RESET "-" +#define PROC_PIO_RXF2_WIDTH _u(32) +#define PROC_PIO_RXF2_MSB _u(31) +#define PROC_PIO_RXF2_LSB _u(0) +#define PROC_PIO_RXF2_ACCESS "RF" +// ============================================================================= +// Register : PROC_PIO_RXF3 +// Description : Direct read access to the RX FIFO for this state machine. Each +// read pops one word from the FIFO. +#define PROC_PIO_RXF3_OFFSET _u(0x00000030) +#define PROC_PIO_RXF3_BITS _u(0xffffffff) +#define PROC_PIO_RXF3_RESET "-" +#define PROC_PIO_RXF3_WIDTH _u(32) +#define PROC_PIO_RXF3_MSB _u(31) +#define PROC_PIO_RXF3_LSB _u(0) +#define PROC_PIO_RXF3_ACCESS "RF" +// ============================================================================= +// Register : PROC_PIO_IRQ +// Description : Interrupt request register. Write 1 to clear +#define PROC_PIO_IRQ_OFFSET _u(0x00000034) +#define PROC_PIO_IRQ_BITS _u(0x000000ff) +#define PROC_PIO_IRQ_RESET _u(0x00000000) +#define PROC_PIO_IRQ_WIDTH _u(32) +#define PROC_PIO_IRQ_MSB _u(7) +#define PROC_PIO_IRQ_LSB _u(0) +#define PROC_PIO_IRQ_ACCESS "WC" +// ============================================================================= +// Register : PROC_PIO_IRQ_FORCE +// Description : Writing a 1 to each of these bits will forcibly assert the +// corresponding IRQ. +// Note this is different to the INTF register: writing here +// affects PIO internal +// state. INTF just asserts the processor-facing IRQ signal for +// testing ISRs, +// and is not visible to the state machines. +#define PROC_PIO_IRQ_FORCE_OFFSET _u(0x00000038) +#define PROC_PIO_IRQ_FORCE_BITS _u(0x000000ff) +#define PROC_PIO_IRQ_FORCE_RESET _u(0x00000000) +#define PROC_PIO_IRQ_FORCE_WIDTH _u(32) +#define PROC_PIO_IRQ_FORCE_MSB _u(7) +#define PROC_PIO_IRQ_FORCE_LSB _u(0) +#define PROC_PIO_IRQ_FORCE_ACCESS "WF" +// ============================================================================= +// Register : PROC_PIO_INPUT_SYNC_BYPASS +// Description : There is a 2-flipflop synchronizer on each GPIO input, which +// protects +// PIO logic from metastabilities. This increases input delay, and +// for fast +// synchronous IO (e.g. SPI) these synchronizers may need to be +// bypassed. +// Each bit in this register corresponds to one GPIO. +// 0 -> input is synchronized (default) +// 1 -> synchronizer is bypassed +// If in doubt, leave this register as all zeroes. +#define PROC_PIO_INPUT_SYNC_BYPASS_OFFSET _u(0x0000003c) +#define PROC_PIO_INPUT_SYNC_BYPASS_BITS _u(0xffffffff) +#define PROC_PIO_INPUT_SYNC_BYPASS_RESET _u(0x00000000) +#define PROC_PIO_INPUT_SYNC_BYPASS_WIDTH _u(32) +#define PROC_PIO_INPUT_SYNC_BYPASS_MSB _u(31) +#define PROC_PIO_INPUT_SYNC_BYPASS_LSB _u(0) +#define PROC_PIO_INPUT_SYNC_BYPASS_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_DBG_PADOUT +// Description : Read to sample the pad output values PIO is currently driving +// to the GPIOs. +#define PROC_PIO_DBG_PADOUT_OFFSET _u(0x00000040) +#define PROC_PIO_DBG_PADOUT_BITS _u(0xffffffff) +#define PROC_PIO_DBG_PADOUT_RESET _u(0x00000000) +#define PROC_PIO_DBG_PADOUT_WIDTH _u(32) +#define PROC_PIO_DBG_PADOUT_MSB _u(31) +#define PROC_PIO_DBG_PADOUT_LSB _u(0) +#define PROC_PIO_DBG_PADOUT_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_DBG_PADOE +// Description : Read to sample the pad output enables (direction) PIO is +// currently driving to the GPIOs. +#define PROC_PIO_DBG_PADOE_OFFSET _u(0x00000044) +#define PROC_PIO_DBG_PADOE_BITS _u(0xffffffff) +#define PROC_PIO_DBG_PADOE_RESET _u(0x00000000) +#define PROC_PIO_DBG_PADOE_WIDTH _u(32) +#define PROC_PIO_DBG_PADOE_MSB _u(31) +#define PROC_PIO_DBG_PADOE_LSB _u(0) +#define PROC_PIO_DBG_PADOE_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_DBG_CFGINFO +// Description : The PIO hardware has some free parameters that may vary between +// chip products. +// These should be provided in the chip datasheet, but are also +// exposed here. +#define PROC_PIO_DBG_CFGINFO_OFFSET _u(0x00000048) +#define PROC_PIO_DBG_CFGINFO_BITS _u(0x003f0f3f) +#define PROC_PIO_DBG_CFGINFO_RESET _u(0x00000000) +#define PROC_PIO_DBG_CFGINFO_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_DBG_CFGINFO_IMEM_SIZE +// Description : The size of the instruction memory, measured in units of one +// instruction +#define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_RESET "-" +#define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_BITS _u(0x003f0000) +#define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_MSB _u(21) +#define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_LSB _u(16) +#define PROC_PIO_DBG_CFGINFO_IMEM_SIZE_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_DBG_CFGINFO_SM_COUNT +// Description : The number of state machines this PIO instance is equipped +// with. +#define PROC_PIO_DBG_CFGINFO_SM_COUNT_RESET "-" +#define PROC_PIO_DBG_CFGINFO_SM_COUNT_BITS _u(0x00000f00) +#define PROC_PIO_DBG_CFGINFO_SM_COUNT_MSB _u(11) +#define PROC_PIO_DBG_CFGINFO_SM_COUNT_LSB _u(8) +#define PROC_PIO_DBG_CFGINFO_SM_COUNT_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_DBG_CFGINFO_FIFO_DEPTH +// Description : The depth of the state machine TX/RX FIFOs, measured in words. +// Joining fifos via SHIFTCTRL_FJOIN gives one FIFO with double +// this depth. +#define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_RESET "-" +#define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_BITS _u(0x0000003f) +#define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_MSB _u(5) +#define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_LSB _u(0) +#define PROC_PIO_DBG_CFGINFO_FIFO_DEPTH_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM0 +// Description : Write-only access to instruction memory location 0 +#define PROC_PIO_INSTR_MEM0_OFFSET _u(0x0000004c) +#define PROC_PIO_INSTR_MEM0_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM0_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM0_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM0_MSB _u(15) +#define PROC_PIO_INSTR_MEM0_LSB _u(0) +#define PROC_PIO_INSTR_MEM0_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM1 +// Description : Write-only access to instruction memory location 1 +#define PROC_PIO_INSTR_MEM1_OFFSET _u(0x00000050) +#define PROC_PIO_INSTR_MEM1_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM1_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM1_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM1_MSB _u(15) +#define PROC_PIO_INSTR_MEM1_LSB _u(0) +#define PROC_PIO_INSTR_MEM1_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM2 +// Description : Write-only access to instruction memory location 2 +#define PROC_PIO_INSTR_MEM2_OFFSET _u(0x00000054) +#define PROC_PIO_INSTR_MEM2_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM2_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM2_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM2_MSB _u(15) +#define PROC_PIO_INSTR_MEM2_LSB _u(0) +#define PROC_PIO_INSTR_MEM2_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM3 +// Description : Write-only access to instruction memory location 3 +#define PROC_PIO_INSTR_MEM3_OFFSET _u(0x00000058) +#define PROC_PIO_INSTR_MEM3_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM3_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM3_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM3_MSB _u(15) +#define PROC_PIO_INSTR_MEM3_LSB _u(0) +#define PROC_PIO_INSTR_MEM3_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM4 +// Description : Write-only access to instruction memory location 4 +#define PROC_PIO_INSTR_MEM4_OFFSET _u(0x0000005c) +#define PROC_PIO_INSTR_MEM4_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM4_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM4_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM4_MSB _u(15) +#define PROC_PIO_INSTR_MEM4_LSB _u(0) +#define PROC_PIO_INSTR_MEM4_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM5 +// Description : Write-only access to instruction memory location 5 +#define PROC_PIO_INSTR_MEM5_OFFSET _u(0x00000060) +#define PROC_PIO_INSTR_MEM5_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM5_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM5_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM5_MSB _u(15) +#define PROC_PIO_INSTR_MEM5_LSB _u(0) +#define PROC_PIO_INSTR_MEM5_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM6 +// Description : Write-only access to instruction memory location 6 +#define PROC_PIO_INSTR_MEM6_OFFSET _u(0x00000064) +#define PROC_PIO_INSTR_MEM6_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM6_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM6_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM6_MSB _u(15) +#define PROC_PIO_INSTR_MEM6_LSB _u(0) +#define PROC_PIO_INSTR_MEM6_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM7 +// Description : Write-only access to instruction memory location 7 +#define PROC_PIO_INSTR_MEM7_OFFSET _u(0x00000068) +#define PROC_PIO_INSTR_MEM7_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM7_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM7_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM7_MSB _u(15) +#define PROC_PIO_INSTR_MEM7_LSB _u(0) +#define PROC_PIO_INSTR_MEM7_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM8 +// Description : Write-only access to instruction memory location 8 +#define PROC_PIO_INSTR_MEM8_OFFSET _u(0x0000006c) +#define PROC_PIO_INSTR_MEM8_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM8_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM8_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM8_MSB _u(15) +#define PROC_PIO_INSTR_MEM8_LSB _u(0) +#define PROC_PIO_INSTR_MEM8_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM9 +// Description : Write-only access to instruction memory location 9 +#define PROC_PIO_INSTR_MEM9_OFFSET _u(0x00000070) +#define PROC_PIO_INSTR_MEM9_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM9_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM9_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM9_MSB _u(15) +#define PROC_PIO_INSTR_MEM9_LSB _u(0) +#define PROC_PIO_INSTR_MEM9_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM10 +// Description : Write-only access to instruction memory location 10 +#define PROC_PIO_INSTR_MEM10_OFFSET _u(0x00000074) +#define PROC_PIO_INSTR_MEM10_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM10_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM10_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM10_MSB _u(15) +#define PROC_PIO_INSTR_MEM10_LSB _u(0) +#define PROC_PIO_INSTR_MEM10_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM11 +// Description : Write-only access to instruction memory location 11 +#define PROC_PIO_INSTR_MEM11_OFFSET _u(0x00000078) +#define PROC_PIO_INSTR_MEM11_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM11_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM11_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM11_MSB _u(15) +#define PROC_PIO_INSTR_MEM11_LSB _u(0) +#define PROC_PIO_INSTR_MEM11_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM12 +// Description : Write-only access to instruction memory location 12 +#define PROC_PIO_INSTR_MEM12_OFFSET _u(0x0000007c) +#define PROC_PIO_INSTR_MEM12_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM12_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM12_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM12_MSB _u(15) +#define PROC_PIO_INSTR_MEM12_LSB _u(0) +#define PROC_PIO_INSTR_MEM12_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM13 +// Description : Write-only access to instruction memory location 13 +#define PROC_PIO_INSTR_MEM13_OFFSET _u(0x00000080) +#define PROC_PIO_INSTR_MEM13_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM13_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM13_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM13_MSB _u(15) +#define PROC_PIO_INSTR_MEM13_LSB _u(0) +#define PROC_PIO_INSTR_MEM13_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM14 +// Description : Write-only access to instruction memory location 14 +#define PROC_PIO_INSTR_MEM14_OFFSET _u(0x00000084) +#define PROC_PIO_INSTR_MEM14_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM14_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM14_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM14_MSB _u(15) +#define PROC_PIO_INSTR_MEM14_LSB _u(0) +#define PROC_PIO_INSTR_MEM14_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM15 +// Description : Write-only access to instruction memory location 15 +#define PROC_PIO_INSTR_MEM15_OFFSET _u(0x00000088) +#define PROC_PIO_INSTR_MEM15_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM15_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM15_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM15_MSB _u(15) +#define PROC_PIO_INSTR_MEM15_LSB _u(0) +#define PROC_PIO_INSTR_MEM15_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM16 +// Description : Write-only access to instruction memory location 16 +#define PROC_PIO_INSTR_MEM16_OFFSET _u(0x0000008c) +#define PROC_PIO_INSTR_MEM16_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM16_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM16_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM16_MSB _u(15) +#define PROC_PIO_INSTR_MEM16_LSB _u(0) +#define PROC_PIO_INSTR_MEM16_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM17 +// Description : Write-only access to instruction memory location 17 +#define PROC_PIO_INSTR_MEM17_OFFSET _u(0x00000090) +#define PROC_PIO_INSTR_MEM17_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM17_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM17_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM17_MSB _u(15) +#define PROC_PIO_INSTR_MEM17_LSB _u(0) +#define PROC_PIO_INSTR_MEM17_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM18 +// Description : Write-only access to instruction memory location 18 +#define PROC_PIO_INSTR_MEM18_OFFSET _u(0x00000094) +#define PROC_PIO_INSTR_MEM18_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM18_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM18_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM18_MSB _u(15) +#define PROC_PIO_INSTR_MEM18_LSB _u(0) +#define PROC_PIO_INSTR_MEM18_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM19 +// Description : Write-only access to instruction memory location 19 +#define PROC_PIO_INSTR_MEM19_OFFSET _u(0x00000098) +#define PROC_PIO_INSTR_MEM19_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM19_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM19_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM19_MSB _u(15) +#define PROC_PIO_INSTR_MEM19_LSB _u(0) +#define PROC_PIO_INSTR_MEM19_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM20 +// Description : Write-only access to instruction memory location 20 +#define PROC_PIO_INSTR_MEM20_OFFSET _u(0x0000009c) +#define PROC_PIO_INSTR_MEM20_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM20_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM20_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM20_MSB _u(15) +#define PROC_PIO_INSTR_MEM20_LSB _u(0) +#define PROC_PIO_INSTR_MEM20_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM21 +// Description : Write-only access to instruction memory location 21 +#define PROC_PIO_INSTR_MEM21_OFFSET _u(0x000000a0) +#define PROC_PIO_INSTR_MEM21_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM21_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM21_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM21_MSB _u(15) +#define PROC_PIO_INSTR_MEM21_LSB _u(0) +#define PROC_PIO_INSTR_MEM21_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM22 +// Description : Write-only access to instruction memory location 22 +#define PROC_PIO_INSTR_MEM22_OFFSET _u(0x000000a4) +#define PROC_PIO_INSTR_MEM22_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM22_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM22_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM22_MSB _u(15) +#define PROC_PIO_INSTR_MEM22_LSB _u(0) +#define PROC_PIO_INSTR_MEM22_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM23 +// Description : Write-only access to instruction memory location 23 +#define PROC_PIO_INSTR_MEM23_OFFSET _u(0x000000a8) +#define PROC_PIO_INSTR_MEM23_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM23_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM23_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM23_MSB _u(15) +#define PROC_PIO_INSTR_MEM23_LSB _u(0) +#define PROC_PIO_INSTR_MEM23_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM24 +// Description : Write-only access to instruction memory location 24 +#define PROC_PIO_INSTR_MEM24_OFFSET _u(0x000000ac) +#define PROC_PIO_INSTR_MEM24_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM24_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM24_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM24_MSB _u(15) +#define PROC_PIO_INSTR_MEM24_LSB _u(0) +#define PROC_PIO_INSTR_MEM24_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM25 +// Description : Write-only access to instruction memory location 25 +#define PROC_PIO_INSTR_MEM25_OFFSET _u(0x000000b0) +#define PROC_PIO_INSTR_MEM25_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM25_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM25_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM25_MSB _u(15) +#define PROC_PIO_INSTR_MEM25_LSB _u(0) +#define PROC_PIO_INSTR_MEM25_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM26 +// Description : Write-only access to instruction memory location 26 +#define PROC_PIO_INSTR_MEM26_OFFSET _u(0x000000b4) +#define PROC_PIO_INSTR_MEM26_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM26_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM26_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM26_MSB _u(15) +#define PROC_PIO_INSTR_MEM26_LSB _u(0) +#define PROC_PIO_INSTR_MEM26_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM27 +// Description : Write-only access to instruction memory location 27 +#define PROC_PIO_INSTR_MEM27_OFFSET _u(0x000000b8) +#define PROC_PIO_INSTR_MEM27_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM27_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM27_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM27_MSB _u(15) +#define PROC_PIO_INSTR_MEM27_LSB _u(0) +#define PROC_PIO_INSTR_MEM27_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM28 +// Description : Write-only access to instruction memory location 28 +#define PROC_PIO_INSTR_MEM28_OFFSET _u(0x000000bc) +#define PROC_PIO_INSTR_MEM28_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM28_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM28_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM28_MSB _u(15) +#define PROC_PIO_INSTR_MEM28_LSB _u(0) +#define PROC_PIO_INSTR_MEM28_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM29 +// Description : Write-only access to instruction memory location 29 +#define PROC_PIO_INSTR_MEM29_OFFSET _u(0x000000c0) +#define PROC_PIO_INSTR_MEM29_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM29_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM29_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM29_MSB _u(15) +#define PROC_PIO_INSTR_MEM29_LSB _u(0) +#define PROC_PIO_INSTR_MEM29_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM30 +// Description : Write-only access to instruction memory location 30 +#define PROC_PIO_INSTR_MEM30_OFFSET _u(0x000000c4) +#define PROC_PIO_INSTR_MEM30_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM30_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM30_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM30_MSB _u(15) +#define PROC_PIO_INSTR_MEM30_LSB _u(0) +#define PROC_PIO_INSTR_MEM30_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_INSTR_MEM31 +// Description : Write-only access to instruction memory location 31 +#define PROC_PIO_INSTR_MEM31_OFFSET _u(0x000000c8) +#define PROC_PIO_INSTR_MEM31_BITS _u(0x0000ffff) +#define PROC_PIO_INSTR_MEM31_RESET _u(0x00000000) +#define PROC_PIO_INSTR_MEM31_WIDTH _u(32) +#define PROC_PIO_INSTR_MEM31_MSB _u(15) +#define PROC_PIO_INSTR_MEM31_LSB _u(0) +#define PROC_PIO_INSTR_MEM31_ACCESS "WO" +// ============================================================================= +// Register : PROC_PIO_SM0_CLKDIV +// Description : Clock divider register for state machine 0 +// Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) +#define PROC_PIO_SM0_CLKDIV_OFFSET _u(0x000000cc) +#define PROC_PIO_SM0_CLKDIV_BITS _u(0xffffff00) +#define PROC_PIO_SM0_CLKDIV_RESET _u(0x00010000) +#define PROC_PIO_SM0_CLKDIV_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_CLKDIV_INT +// Description : Effective frequency is sysclk/int. +// Value of 0 is interpreted as max possible value +#define PROC_PIO_SM0_CLKDIV_INT_RESET _u(0x0001) +#define PROC_PIO_SM0_CLKDIV_INT_BITS _u(0xffff0000) +#define PROC_PIO_SM0_CLKDIV_INT_MSB _u(31) +#define PROC_PIO_SM0_CLKDIV_INT_LSB _u(16) +#define PROC_PIO_SM0_CLKDIV_INT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_CLKDIV_FRAC +// Description : Fractional part of clock divider +#define PROC_PIO_SM0_CLKDIV_FRAC_RESET _u(0x00) +#define PROC_PIO_SM0_CLKDIV_FRAC_BITS _u(0x0000ff00) +#define PROC_PIO_SM0_CLKDIV_FRAC_MSB _u(15) +#define PROC_PIO_SM0_CLKDIV_FRAC_LSB _u(8) +#define PROC_PIO_SM0_CLKDIV_FRAC_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM0_EXECCTRL +// Description : Execution/behavioural settings for state machine 0 +#define PROC_PIO_SM0_EXECCTRL_OFFSET _u(0x000000d0) +#define PROC_PIO_SM0_EXECCTRL_BITS _u(0xffffffbf) +#define PROC_PIO_SM0_EXECCTRL_RESET _u(0x0001f000) +#define PROC_PIO_SM0_EXECCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_EXEC_STALLED +// Description : An instruction written to SMx_INSTR is stalled, and latched by +// the +// state machine. Will clear once the instruction completes. +#define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_RESET _u(0x0) +#define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_BITS _u(0x80000000) +#define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_MSB _u(31) +#define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_LSB _u(31) +#define PROC_PIO_SM0_EXECCTRL_EXEC_STALLED_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_SIDE_EN +// Description : If 1, the delay MSB is used as side-set enable, rather than a +// side-set data bit. This allows instructions to perform side-set +// optionally, +// rather than on every instruction. +#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_RESET _u(0x0) +#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS _u(0x40000000) +#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_MSB _u(30) +#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB _u(30) +#define PROC_PIO_SM0_EXECCTRL_SIDE_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR +// Description : Side-set data is asserted to pin OEs instead of pin values +#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_RESET _u(0x0) +#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS _u(0x20000000) +#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_MSB _u(29) +#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB _u(29) +#define PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_JMP_PIN +// Description : The GPIO number to use as condition for JMP PIN. Unaffected by +// input mapping. +#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_RESET _u(0x00) +#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS _u(0x1f000000) +#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_MSB _u(28) +#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB _u(24) +#define PROC_PIO_SM0_EXECCTRL_JMP_PIN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL +// Description : Which data bit to use for inline OUT enable +#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_RESET _u(0x00) +#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS _u(0x00f80000) +#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_MSB _u(23) +#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB _u(19) +#define PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN +// Description : If 1, use a bit of OUT data as an auxiliary write enable +// When used in conjunction with OUT_STICKY, writes with an enable +// of 0 will +// deassert the latest pin write. This can create useful +// masking/override behaviour +// due to the priority ordering of state machine pin writes (SM0 < +// SM1 < ...) +#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_RESET _u(0x0) +#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS _u(0x00040000) +#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_MSB _u(18) +#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB _u(18) +#define PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_OUT_STICKY +// Description : Continuously assert the most recent OUT/SET to the pins +#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_RESET _u(0x0) +#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS _u(0x00020000) +#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_MSB _u(17) +#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB _u(17) +#define PROC_PIO_SM0_EXECCTRL_OUT_STICKY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_WRAP_TOP +// Description : After reaching this address, execution is wrapped to +// wrap_bottom. +// If the instruction is a jump, and the jump condition is true, +// the jump takes priority. +#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_RESET _u(0x1f) +#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS _u(0x0001f000) +#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_MSB _u(16) +#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB _u(12) +#define PROC_PIO_SM0_EXECCTRL_WRAP_TOP_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM +// Description : After reaching wrap_top, execution is wrapped to this address. +#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_RESET _u(0x00) +#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS _u(0x00000f80) +#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_MSB _u(11) +#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB _u(7) +#define PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_STATUS_SEL +// Description : Comparison used for the MOV x, STATUS instruction. +// 0x0 -> All-ones if TX FIFO level < N, otherwise all-zeroes +// 0x1 -> All-ones if RX FIFO level < N, otherwise all-zeroes +#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_RESET _u(0x0) +#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS _u(0x00000020) +#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_MSB _u(5) +#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB _u(5) +#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_ACCESS "RW" +#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_VALUE_TXLEVEL _u(0x0) +#define PROC_PIO_SM0_EXECCTRL_STATUS_SEL_VALUE_RXLEVEL _u(0x1) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_EXECCTRL_STATUS_N +// Description : Comparison level for the MOV x, STATUS instruction +#define PROC_PIO_SM0_EXECCTRL_STATUS_N_RESET _u(0x00) +#define PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS _u(0x0000001f) +#define PROC_PIO_SM0_EXECCTRL_STATUS_N_MSB _u(4) +#define PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB _u(0) +#define PROC_PIO_SM0_EXECCTRL_STATUS_N_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM0_SHIFTCTRL +// Description : Control behaviour of the input/output shift registers for state +// machine 0 +#define PROC_PIO_SM0_SHIFTCTRL_OFFSET _u(0x000000d4) +#define PROC_PIO_SM0_SHIFTCTRL_BITS _u(0xffff0000) +#define PROC_PIO_SM0_SHIFTCTRL_RESET _u(0x000c0000) +#define PROC_PIO_SM0_SHIFTCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX +// Description : When 1, RX FIFO steals the TX FIFO's storage, and becomes twice +// as deep. +// TX FIFO is disabled as a result (always reads as both full and +// empty). +// FIFOs are flushed when this bit is changed. +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_RESET _u(0x0) +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS _u(0x80000000) +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_MSB _u(31) +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_LSB _u(31) +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX +// Description : When 1, TX FIFO steals the RX FIFO's storage, and becomes twice +// as deep. +// RX FIFO is disabled as a result (always reads as both full and +// empty). +// FIFOs are flushed when this bit is changed. +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_RESET _u(0x0) +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS _u(0x40000000) +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_MSB _u(30) +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB _u(30) +#define PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH +// Description : Number of bits shifted out of TXSR before autopull or +// conditional pull. +// Write 0 for value of 32. +#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_RESET _u(0x00) +#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS _u(0x3e000000) +#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_MSB _u(29) +#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB _u(25) +#define PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH +// Description : Number of bits shifted into RXSR before autopush or conditional +// push. +// Write 0 for value of 32. +#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_RESET _u(0x00) +#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS _u(0x01f00000) +#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_MSB _u(24) +#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB _u(20) +#define PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR +// Description : 1 = shift out of output shift register to right. 0 = to left. +#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_RESET _u(0x1) +#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS _u(0x00080000) +#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_MSB _u(19) +#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB _u(19) +#define PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR +// Description : 1 = shift input shift register to right (data enters from +// left). 0 = to left. +#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_RESET _u(0x1) +#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS _u(0x00040000) +#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_MSB _u(18) +#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB _u(18) +#define PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_SHIFTCTRL_AUTOPULL +// Description : Pull automatically when the output shift register is emptied +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_RESET _u(0x0) +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS _u(0x00020000) +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_MSB _u(17) +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB _u(17) +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH +// Description : Push automatically when the input shift register is filled +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_RESET _u(0x0) +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS _u(0x00010000) +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_MSB _u(16) +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB _u(16) +#define PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM0_ADDR +// Description : Current instruction address of state machine 0 +#define PROC_PIO_SM0_ADDR_OFFSET _u(0x000000d8) +#define PROC_PIO_SM0_ADDR_BITS _u(0x0000001f) +#define PROC_PIO_SM0_ADDR_RESET _u(0x00000000) +#define PROC_PIO_SM0_ADDR_WIDTH _u(32) +#define PROC_PIO_SM0_ADDR_MSB _u(4) +#define PROC_PIO_SM0_ADDR_LSB _u(0) +#define PROC_PIO_SM0_ADDR_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_SM0_INSTR +// Description : Instruction currently being executed by state machine 0 +// Write to execute an instruction immediately (including jumps) +// and then resume execution. +#define PROC_PIO_SM0_INSTR_OFFSET _u(0x000000dc) +#define PROC_PIO_SM0_INSTR_BITS _u(0x0000ffff) +#define PROC_PIO_SM0_INSTR_RESET "-" +#define PROC_PIO_SM0_INSTR_WIDTH _u(32) +#define PROC_PIO_SM0_INSTR_MSB _u(15) +#define PROC_PIO_SM0_INSTR_LSB _u(0) +#define PROC_PIO_SM0_INSTR_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM0_PINCTRL +// Description : State machine pin control +#define PROC_PIO_SM0_PINCTRL_OFFSET _u(0x000000e0) +#define PROC_PIO_SM0_PINCTRL_BITS _u(0xffffffff) +#define PROC_PIO_SM0_PINCTRL_RESET _u(0x14000000) +#define PROC_PIO_SM0_PINCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_PINCTRL_SIDESET_COUNT +// Description : The number of delay bits co-opted for side-set. Inclusive of +// the enable bit, if present. +#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_RESET _u(0x0) +#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS _u(0xe0000000) +#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_MSB _u(31) +#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB _u(29) +#define PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_PINCTRL_SET_COUNT +// Description : The number of pins asserted by a SET. Max of 5 +#define PROC_PIO_SM0_PINCTRL_SET_COUNT_RESET _u(0x5) +#define PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS _u(0x1c000000) +#define PROC_PIO_SM0_PINCTRL_SET_COUNT_MSB _u(28) +#define PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB _u(26) +#define PROC_PIO_SM0_PINCTRL_SET_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_PINCTRL_OUT_COUNT +// Description : The number of pins asserted by an OUT. Value of 0 -> 32 pins +#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_RESET _u(0x00) +#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS _u(0x03f00000) +#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_MSB _u(25) +#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB _u(20) +#define PROC_PIO_SM0_PINCTRL_OUT_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_PINCTRL_IN_BASE +// Description : The virtual pin corresponding to IN bit 0 +#define PROC_PIO_SM0_PINCTRL_IN_BASE_RESET _u(0x00) +#define PROC_PIO_SM0_PINCTRL_IN_BASE_BITS _u(0x000f8000) +#define PROC_PIO_SM0_PINCTRL_IN_BASE_MSB _u(19) +#define PROC_PIO_SM0_PINCTRL_IN_BASE_LSB _u(15) +#define PROC_PIO_SM0_PINCTRL_IN_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_PINCTRL_SIDESET_BASE +// Description : The virtual pin corresponding to delay field bit 0 +#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_RESET _u(0x00) +#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS _u(0x00007c00) +#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_MSB _u(14) +#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB _u(10) +#define PROC_PIO_SM0_PINCTRL_SIDESET_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_PINCTRL_SET_BASE +// Description : The virtual pin corresponding to SET bit 0 +#define PROC_PIO_SM0_PINCTRL_SET_BASE_RESET _u(0x00) +#define PROC_PIO_SM0_PINCTRL_SET_BASE_BITS _u(0x000003e0) +#define PROC_PIO_SM0_PINCTRL_SET_BASE_MSB _u(9) +#define PROC_PIO_SM0_PINCTRL_SET_BASE_LSB _u(5) +#define PROC_PIO_SM0_PINCTRL_SET_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_PINCTRL_OUT_BASE +// Description : The virtual pin corresponding to OUT bit 0 +#define PROC_PIO_SM0_PINCTRL_OUT_BASE_RESET _u(0x00) +#define PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS _u(0x0000001f) +#define PROC_PIO_SM0_PINCTRL_OUT_BASE_MSB _u(4) +#define PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB _u(0) +#define PROC_PIO_SM0_PINCTRL_OUT_BASE_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM0_DMACTRL_TX +// Description : State machine DMA control +#define PROC_PIO_SM0_DMACTRL_TX_OFFSET _u(0x000000e4) +#define PROC_PIO_SM0_DMACTRL_TX_BITS _u(0xc0000f9f) +#define PROC_PIO_SM0_DMACTRL_TX_RESET _u(0x00000104) +#define PROC_PIO_SM0_DMACTRL_TX_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_DMACTRL_TX_DREQ_EN +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_RESET _u(0x0) +#define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_BITS _u(0x80000000) +#define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_MSB _u(31) +#define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_LSB _u(31) +#define PROC_PIO_SM0_DMACTRL_TX_DREQ_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_DMACTRL_TX_ACTIVE +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_RESET "-" +#define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_BITS _u(0x40000000) +#define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_MSB _u(30) +#define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_LSB _u(30) +#define PROC_PIO_SM0_DMACTRL_TX_ACTIVE_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME +// Description : Delay in number of bus cycles before successive DREQs are +// generated. +// Used to account for system bus latency in write data arriving +// at the FIFO. +#define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_RESET _u(0x02) +#define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_BITS _u(0x00000f80) +#define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_MSB _u(11) +#define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_LSB _u(7) +#define PROC_PIO_SM0_DMACTRL_TX_DWELL_TIME_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD +// Description : Threshold control. If there are no more than THRESHOLD items in +// the TX FIFO, DMA dreq and/or the interrupt line is asserted. +#define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_RESET _u(0x04) +#define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_BITS _u(0x0000001f) +#define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_MSB _u(4) +#define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_LSB _u(0) +#define PROC_PIO_SM0_DMACTRL_TX_FIFO_THRESHOLD_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM0_DMACTRL_RX +// Description : State machine DMA control +#define PROC_PIO_SM0_DMACTRL_RX_OFFSET _u(0x000000e8) +#define PROC_PIO_SM0_DMACTRL_RX_BITS _u(0xc0000f9f) +#define PROC_PIO_SM0_DMACTRL_RX_RESET _u(0x00000104) +#define PROC_PIO_SM0_DMACTRL_RX_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_DMACTRL_RX_DREQ_EN +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_RESET _u(0x0) +#define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_BITS _u(0x80000000) +#define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_MSB _u(31) +#define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_LSB _u(31) +#define PROC_PIO_SM0_DMACTRL_RX_DREQ_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_DMACTRL_RX_ACTIVE +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_RESET "-" +#define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_BITS _u(0x40000000) +#define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_MSB _u(30) +#define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_LSB _u(30) +#define PROC_PIO_SM0_DMACTRL_RX_ACTIVE_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME +// Description : Delay in number of bus cycles before successive DREQs are +// generated. +// Used to account for system bus latency in write data arriving +// at the FIFO. +#define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_RESET _u(0x02) +#define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_BITS _u(0x00000f80) +#define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_MSB _u(11) +#define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_LSB _u(7) +#define PROC_PIO_SM0_DMACTRL_RX_DWELL_TIME_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD +// Description : Threshold control. If there are at least THRESHOLD items in the +// RX FIFO, DMA dreq and/or the interrupt line is asserted. +#define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_RESET _u(0x04) +#define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_BITS _u(0x0000001f) +#define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_MSB _u(4) +#define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_LSB _u(0) +#define PROC_PIO_SM0_DMACTRL_RX_FIFO_THRESHOLD_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM1_CLKDIV +// Description : Clock divider register for state machine 1 +// Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) +#define PROC_PIO_SM1_CLKDIV_OFFSET _u(0x000000ec) +#define PROC_PIO_SM1_CLKDIV_BITS _u(0xffffff00) +#define PROC_PIO_SM1_CLKDIV_RESET _u(0x00010000) +#define PROC_PIO_SM1_CLKDIV_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_CLKDIV_INT +// Description : Effective frequency is sysclk/int. +// Value of 0 is interpreted as max possible value +#define PROC_PIO_SM1_CLKDIV_INT_RESET _u(0x0001) +#define PROC_PIO_SM1_CLKDIV_INT_BITS _u(0xffff0000) +#define PROC_PIO_SM1_CLKDIV_INT_MSB _u(31) +#define PROC_PIO_SM1_CLKDIV_INT_LSB _u(16) +#define PROC_PIO_SM1_CLKDIV_INT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_CLKDIV_FRAC +// Description : Fractional part of clock divider +#define PROC_PIO_SM1_CLKDIV_FRAC_RESET _u(0x00) +#define PROC_PIO_SM1_CLKDIV_FRAC_BITS _u(0x0000ff00) +#define PROC_PIO_SM1_CLKDIV_FRAC_MSB _u(15) +#define PROC_PIO_SM1_CLKDIV_FRAC_LSB _u(8) +#define PROC_PIO_SM1_CLKDIV_FRAC_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM1_EXECCTRL +// Description : Execution/behavioural settings for state machine 1 +#define PROC_PIO_SM1_EXECCTRL_OFFSET _u(0x000000f0) +#define PROC_PIO_SM1_EXECCTRL_BITS _u(0xffffffbf) +#define PROC_PIO_SM1_EXECCTRL_RESET _u(0x0001f000) +#define PROC_PIO_SM1_EXECCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_EXEC_STALLED +// Description : An instruction written to SMx_INSTR is stalled, and latched by +// the +// state machine. Will clear once the instruction completes. +#define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_RESET _u(0x0) +#define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_BITS _u(0x80000000) +#define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_MSB _u(31) +#define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_LSB _u(31) +#define PROC_PIO_SM1_EXECCTRL_EXEC_STALLED_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_SIDE_EN +// Description : If 1, the delay MSB is used as side-set enable, rather than a +// side-set data bit. This allows instructions to perform side-set +// optionally, +// rather than on every instruction. +#define PROC_PIO_SM1_EXECCTRL_SIDE_EN_RESET _u(0x0) +#define PROC_PIO_SM1_EXECCTRL_SIDE_EN_BITS _u(0x40000000) +#define PROC_PIO_SM1_EXECCTRL_SIDE_EN_MSB _u(30) +#define PROC_PIO_SM1_EXECCTRL_SIDE_EN_LSB _u(30) +#define PROC_PIO_SM1_EXECCTRL_SIDE_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR +// Description : Side-set data is asserted to pin OEs instead of pin values +#define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_RESET _u(0x0) +#define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_BITS _u(0x20000000) +#define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_MSB _u(29) +#define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_LSB _u(29) +#define PROC_PIO_SM1_EXECCTRL_SIDE_PINDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_JMP_PIN +// Description : The GPIO number to use as condition for JMP PIN. Unaffected by +// input mapping. +#define PROC_PIO_SM1_EXECCTRL_JMP_PIN_RESET _u(0x00) +#define PROC_PIO_SM1_EXECCTRL_JMP_PIN_BITS _u(0x1f000000) +#define PROC_PIO_SM1_EXECCTRL_JMP_PIN_MSB _u(28) +#define PROC_PIO_SM1_EXECCTRL_JMP_PIN_LSB _u(24) +#define PROC_PIO_SM1_EXECCTRL_JMP_PIN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL +// Description : Which data bit to use for inline OUT enable +#define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_RESET _u(0x00) +#define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_BITS _u(0x00f80000) +#define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_MSB _u(23) +#define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_LSB _u(19) +#define PROC_PIO_SM1_EXECCTRL_OUT_EN_SEL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN +// Description : If 1, use a bit of OUT data as an auxiliary write enable +// When used in conjunction with OUT_STICKY, writes with an enable +// of 0 will +// deassert the latest pin write. This can create useful +// masking/override behaviour +// due to the priority ordering of state machine pin writes (SM0 < +// SM1 < ...) +#define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_RESET _u(0x0) +#define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_BITS _u(0x00040000) +#define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_MSB _u(18) +#define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_LSB _u(18) +#define PROC_PIO_SM1_EXECCTRL_INLINE_OUT_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_OUT_STICKY +// Description : Continuously assert the most recent OUT/SET to the pins +#define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_RESET _u(0x0) +#define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_BITS _u(0x00020000) +#define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_MSB _u(17) +#define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_LSB _u(17) +#define PROC_PIO_SM1_EXECCTRL_OUT_STICKY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_WRAP_TOP +// Description : After reaching this address, execution is wrapped to +// wrap_bottom. +// If the instruction is a jump, and the jump condition is true, +// the jump takes priority. +#define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_RESET _u(0x1f) +#define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_BITS _u(0x0001f000) +#define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_MSB _u(16) +#define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_LSB _u(12) +#define PROC_PIO_SM1_EXECCTRL_WRAP_TOP_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM +// Description : After reaching wrap_top, execution is wrapped to this address. +#define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_RESET _u(0x00) +#define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_BITS _u(0x00000f80) +#define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_MSB _u(11) +#define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_LSB _u(7) +#define PROC_PIO_SM1_EXECCTRL_WRAP_BOTTOM_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_STATUS_SEL +// Description : Comparison used for the MOV x, STATUS instruction. +// 0x0 -> All-ones if TX FIFO level < N, otherwise all-zeroes +// 0x1 -> All-ones if RX FIFO level < N, otherwise all-zeroes +#define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_RESET _u(0x0) +#define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_BITS _u(0x00000020) +#define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_MSB _u(5) +#define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_LSB _u(5) +#define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_ACCESS "RW" +#define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_VALUE_TXLEVEL _u(0x0) +#define PROC_PIO_SM1_EXECCTRL_STATUS_SEL_VALUE_RXLEVEL _u(0x1) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_EXECCTRL_STATUS_N +// Description : Comparison level for the MOV x, STATUS instruction +#define PROC_PIO_SM1_EXECCTRL_STATUS_N_RESET _u(0x00) +#define PROC_PIO_SM1_EXECCTRL_STATUS_N_BITS _u(0x0000001f) +#define PROC_PIO_SM1_EXECCTRL_STATUS_N_MSB _u(4) +#define PROC_PIO_SM1_EXECCTRL_STATUS_N_LSB _u(0) +#define PROC_PIO_SM1_EXECCTRL_STATUS_N_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM1_SHIFTCTRL +// Description : Control behaviour of the input/output shift registers for state +// machine 1 +#define PROC_PIO_SM1_SHIFTCTRL_OFFSET _u(0x000000f4) +#define PROC_PIO_SM1_SHIFTCTRL_BITS _u(0xffff0000) +#define PROC_PIO_SM1_SHIFTCTRL_RESET _u(0x000c0000) +#define PROC_PIO_SM1_SHIFTCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX +// Description : When 1, RX FIFO steals the TX FIFO's storage, and becomes twice +// as deep. +// TX FIFO is disabled as a result (always reads as both full and +// empty). +// FIFOs are flushed when this bit is changed. +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_RESET _u(0x0) +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_BITS _u(0x80000000) +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_MSB _u(31) +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_LSB _u(31) +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_RX_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX +// Description : When 1, TX FIFO steals the RX FIFO's storage, and becomes twice +// as deep. +// RX FIFO is disabled as a result (always reads as both full and +// empty). +// FIFOs are flushed when this bit is changed. +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_RESET _u(0x0) +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_BITS _u(0x40000000) +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_MSB _u(30) +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_LSB _u(30) +#define PROC_PIO_SM1_SHIFTCTRL_FJOIN_TX_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH +// Description : Number of bits shifted out of TXSR before autopull or +// conditional pull. +// Write 0 for value of 32. +#define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_RESET _u(0x00) +#define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_BITS _u(0x3e000000) +#define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_MSB _u(29) +#define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_LSB _u(25) +#define PROC_PIO_SM1_SHIFTCTRL_PULL_THRESH_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH +// Description : Number of bits shifted into RXSR before autopush or conditional +// push. +// Write 0 for value of 32. +#define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_RESET _u(0x00) +#define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_BITS _u(0x01f00000) +#define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_MSB _u(24) +#define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_LSB _u(20) +#define PROC_PIO_SM1_SHIFTCTRL_PUSH_THRESH_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR +// Description : 1 = shift out of output shift register to right. 0 = to left. +#define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_RESET _u(0x1) +#define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_BITS _u(0x00080000) +#define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_MSB _u(19) +#define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_LSB _u(19) +#define PROC_PIO_SM1_SHIFTCTRL_OUT_SHIFTDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR +// Description : 1 = shift input shift register to right (data enters from +// left). 0 = to left. +#define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_RESET _u(0x1) +#define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_BITS _u(0x00040000) +#define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_MSB _u(18) +#define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_LSB _u(18) +#define PROC_PIO_SM1_SHIFTCTRL_IN_SHIFTDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_SHIFTCTRL_AUTOPULL +// Description : Pull automatically when the output shift register is emptied +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_RESET _u(0x0) +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_BITS _u(0x00020000) +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_MSB _u(17) +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_LSB _u(17) +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH +// Description : Push automatically when the input shift register is filled +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_RESET _u(0x0) +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_BITS _u(0x00010000) +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_MSB _u(16) +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_LSB _u(16) +#define PROC_PIO_SM1_SHIFTCTRL_AUTOPUSH_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM1_ADDR +// Description : Current instruction address of state machine 1 +#define PROC_PIO_SM1_ADDR_OFFSET _u(0x000000f8) +#define PROC_PIO_SM1_ADDR_BITS _u(0x0000001f) +#define PROC_PIO_SM1_ADDR_RESET _u(0x00000000) +#define PROC_PIO_SM1_ADDR_WIDTH _u(32) +#define PROC_PIO_SM1_ADDR_MSB _u(4) +#define PROC_PIO_SM1_ADDR_LSB _u(0) +#define PROC_PIO_SM1_ADDR_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_SM1_INSTR +// Description : Instruction currently being executed by state machine 1 +// Write to execute an instruction immediately (including jumps) +// and then resume execution. +#define PROC_PIO_SM1_INSTR_OFFSET _u(0x000000fc) +#define PROC_PIO_SM1_INSTR_BITS _u(0x0000ffff) +#define PROC_PIO_SM1_INSTR_RESET "-" +#define PROC_PIO_SM1_INSTR_WIDTH _u(32) +#define PROC_PIO_SM1_INSTR_MSB _u(15) +#define PROC_PIO_SM1_INSTR_LSB _u(0) +#define PROC_PIO_SM1_INSTR_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM1_PINCTRL +// Description : State machine pin control +#define PROC_PIO_SM1_PINCTRL_OFFSET _u(0x00000100) +#define PROC_PIO_SM1_PINCTRL_BITS _u(0xffffffff) +#define PROC_PIO_SM1_PINCTRL_RESET _u(0x14000000) +#define PROC_PIO_SM1_PINCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_PINCTRL_SIDESET_COUNT +// Description : The number of delay bits co-opted for side-set. Inclusive of +// the enable bit, if present. +#define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_RESET _u(0x0) +#define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_BITS _u(0xe0000000) +#define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_MSB _u(31) +#define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_LSB _u(29) +#define PROC_PIO_SM1_PINCTRL_SIDESET_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_PINCTRL_SET_COUNT +// Description : The number of pins asserted by a SET. Max of 5 +#define PROC_PIO_SM1_PINCTRL_SET_COUNT_RESET _u(0x5) +#define PROC_PIO_SM1_PINCTRL_SET_COUNT_BITS _u(0x1c000000) +#define PROC_PIO_SM1_PINCTRL_SET_COUNT_MSB _u(28) +#define PROC_PIO_SM1_PINCTRL_SET_COUNT_LSB _u(26) +#define PROC_PIO_SM1_PINCTRL_SET_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_PINCTRL_OUT_COUNT +// Description : The number of pins asserted by an OUT. Value of 0 -> 32 pins +#define PROC_PIO_SM1_PINCTRL_OUT_COUNT_RESET _u(0x00) +#define PROC_PIO_SM1_PINCTRL_OUT_COUNT_BITS _u(0x03f00000) +#define PROC_PIO_SM1_PINCTRL_OUT_COUNT_MSB _u(25) +#define PROC_PIO_SM1_PINCTRL_OUT_COUNT_LSB _u(20) +#define PROC_PIO_SM1_PINCTRL_OUT_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_PINCTRL_IN_BASE +// Description : The virtual pin corresponding to IN bit 0 +#define PROC_PIO_SM1_PINCTRL_IN_BASE_RESET _u(0x00) +#define PROC_PIO_SM1_PINCTRL_IN_BASE_BITS _u(0x000f8000) +#define PROC_PIO_SM1_PINCTRL_IN_BASE_MSB _u(19) +#define PROC_PIO_SM1_PINCTRL_IN_BASE_LSB _u(15) +#define PROC_PIO_SM1_PINCTRL_IN_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_PINCTRL_SIDESET_BASE +// Description : The virtual pin corresponding to delay field bit 0 +#define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_RESET _u(0x00) +#define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_BITS _u(0x00007c00) +#define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_MSB _u(14) +#define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_LSB _u(10) +#define PROC_PIO_SM1_PINCTRL_SIDESET_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_PINCTRL_SET_BASE +// Description : The virtual pin corresponding to SET bit 0 +#define PROC_PIO_SM1_PINCTRL_SET_BASE_RESET _u(0x00) +#define PROC_PIO_SM1_PINCTRL_SET_BASE_BITS _u(0x000003e0) +#define PROC_PIO_SM1_PINCTRL_SET_BASE_MSB _u(9) +#define PROC_PIO_SM1_PINCTRL_SET_BASE_LSB _u(5) +#define PROC_PIO_SM1_PINCTRL_SET_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_PINCTRL_OUT_BASE +// Description : The virtual pin corresponding to OUT bit 0 +#define PROC_PIO_SM1_PINCTRL_OUT_BASE_RESET _u(0x00) +#define PROC_PIO_SM1_PINCTRL_OUT_BASE_BITS _u(0x0000001f) +#define PROC_PIO_SM1_PINCTRL_OUT_BASE_MSB _u(4) +#define PROC_PIO_SM1_PINCTRL_OUT_BASE_LSB _u(0) +#define PROC_PIO_SM1_PINCTRL_OUT_BASE_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM1_DMACTRL_TX +// Description : State machine DMA control +#define PROC_PIO_SM1_DMACTRL_TX_OFFSET _u(0x00000104) +#define PROC_PIO_SM1_DMACTRL_TX_BITS _u(0xc0000f9f) +#define PROC_PIO_SM1_DMACTRL_TX_RESET _u(0x00000104) +#define PROC_PIO_SM1_DMACTRL_TX_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_DMACTRL_TX_DREQ_EN +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_RESET _u(0x0) +#define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_BITS _u(0x80000000) +#define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_MSB _u(31) +#define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_LSB _u(31) +#define PROC_PIO_SM1_DMACTRL_TX_DREQ_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_DMACTRL_TX_ACTIVE +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_RESET "-" +#define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_BITS _u(0x40000000) +#define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_MSB _u(30) +#define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_LSB _u(30) +#define PROC_PIO_SM1_DMACTRL_TX_ACTIVE_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME +// Description : Delay in number of bus cycles before successive DREQs are +// generated. +// Used to account for system bus latency in write data arriving +// at the FIFO. +#define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_RESET _u(0x02) +#define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_BITS _u(0x00000f80) +#define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_MSB _u(11) +#define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_LSB _u(7) +#define PROC_PIO_SM1_DMACTRL_TX_DWELL_TIME_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD +// Description : Threshold control. If there are no more than THRESHOLD items in +// the TX FIFO, DMA dreq and/or the interrupt line is asserted. +#define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_RESET _u(0x04) +#define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_BITS _u(0x0000001f) +#define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_MSB _u(4) +#define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_LSB _u(0) +#define PROC_PIO_SM1_DMACTRL_TX_FIFO_THRESHOLD_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM1_DMACTRL_RX +// Description : State machine DMA control +#define PROC_PIO_SM1_DMACTRL_RX_OFFSET _u(0x00000108) +#define PROC_PIO_SM1_DMACTRL_RX_BITS _u(0xc0000f9f) +#define PROC_PIO_SM1_DMACTRL_RX_RESET _u(0x00000104) +#define PROC_PIO_SM1_DMACTRL_RX_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_DMACTRL_RX_DREQ_EN +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_RESET _u(0x0) +#define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_BITS _u(0x80000000) +#define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_MSB _u(31) +#define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_LSB _u(31) +#define PROC_PIO_SM1_DMACTRL_RX_DREQ_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_DMACTRL_RX_ACTIVE +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_RESET "-" +#define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_BITS _u(0x40000000) +#define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_MSB _u(30) +#define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_LSB _u(30) +#define PROC_PIO_SM1_DMACTRL_RX_ACTIVE_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME +// Description : Delay in number of bus cycles before successive DREQs are +// generated. +// Used to account for system bus latency in write data arriving +// at the FIFO. +#define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_RESET _u(0x02) +#define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_BITS _u(0x00000f80) +#define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_MSB _u(11) +#define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_LSB _u(7) +#define PROC_PIO_SM1_DMACTRL_RX_DWELL_TIME_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD +// Description : Threshold control. If there are at least THRESHOLD items in the +// RX FIFO, DMA dreq and/or the interrupt line is asserted. +#define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_RESET _u(0x04) +#define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_BITS _u(0x0000001f) +#define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_MSB _u(4) +#define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_LSB _u(0) +#define PROC_PIO_SM1_DMACTRL_RX_FIFO_THRESHOLD_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM2_CLKDIV +// Description : Clock divider register for state machine 2 +// Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) +#define PROC_PIO_SM2_CLKDIV_OFFSET _u(0x0000010c) +#define PROC_PIO_SM2_CLKDIV_BITS _u(0xffffff00) +#define PROC_PIO_SM2_CLKDIV_RESET _u(0x00010000) +#define PROC_PIO_SM2_CLKDIV_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_CLKDIV_INT +// Description : Effective frequency is sysclk/int. +// Value of 0 is interpreted as max possible value +#define PROC_PIO_SM2_CLKDIV_INT_RESET _u(0x0001) +#define PROC_PIO_SM2_CLKDIV_INT_BITS _u(0xffff0000) +#define PROC_PIO_SM2_CLKDIV_INT_MSB _u(31) +#define PROC_PIO_SM2_CLKDIV_INT_LSB _u(16) +#define PROC_PIO_SM2_CLKDIV_INT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_CLKDIV_FRAC +// Description : Fractional part of clock divider +#define PROC_PIO_SM2_CLKDIV_FRAC_RESET _u(0x00) +#define PROC_PIO_SM2_CLKDIV_FRAC_BITS _u(0x0000ff00) +#define PROC_PIO_SM2_CLKDIV_FRAC_MSB _u(15) +#define PROC_PIO_SM2_CLKDIV_FRAC_LSB _u(8) +#define PROC_PIO_SM2_CLKDIV_FRAC_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM2_EXECCTRL +// Description : Execution/behavioural settings for state machine 2 +#define PROC_PIO_SM2_EXECCTRL_OFFSET _u(0x00000110) +#define PROC_PIO_SM2_EXECCTRL_BITS _u(0xffffffbf) +#define PROC_PIO_SM2_EXECCTRL_RESET _u(0x0001f000) +#define PROC_PIO_SM2_EXECCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_EXEC_STALLED +// Description : An instruction written to SMx_INSTR is stalled, and latched by +// the +// state machine. Will clear once the instruction completes. +#define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_RESET _u(0x0) +#define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_BITS _u(0x80000000) +#define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_MSB _u(31) +#define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_LSB _u(31) +#define PROC_PIO_SM2_EXECCTRL_EXEC_STALLED_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_SIDE_EN +// Description : If 1, the delay MSB is used as side-set enable, rather than a +// side-set data bit. This allows instructions to perform side-set +// optionally, +// rather than on every instruction. +#define PROC_PIO_SM2_EXECCTRL_SIDE_EN_RESET _u(0x0) +#define PROC_PIO_SM2_EXECCTRL_SIDE_EN_BITS _u(0x40000000) +#define PROC_PIO_SM2_EXECCTRL_SIDE_EN_MSB _u(30) +#define PROC_PIO_SM2_EXECCTRL_SIDE_EN_LSB _u(30) +#define PROC_PIO_SM2_EXECCTRL_SIDE_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR +// Description : Side-set data is asserted to pin OEs instead of pin values +#define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_RESET _u(0x0) +#define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_BITS _u(0x20000000) +#define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_MSB _u(29) +#define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_LSB _u(29) +#define PROC_PIO_SM2_EXECCTRL_SIDE_PINDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_JMP_PIN +// Description : The GPIO number to use as condition for JMP PIN. Unaffected by +// input mapping. +#define PROC_PIO_SM2_EXECCTRL_JMP_PIN_RESET _u(0x00) +#define PROC_PIO_SM2_EXECCTRL_JMP_PIN_BITS _u(0x1f000000) +#define PROC_PIO_SM2_EXECCTRL_JMP_PIN_MSB _u(28) +#define PROC_PIO_SM2_EXECCTRL_JMP_PIN_LSB _u(24) +#define PROC_PIO_SM2_EXECCTRL_JMP_PIN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL +// Description : Which data bit to use for inline OUT enable +#define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_RESET _u(0x00) +#define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_BITS _u(0x00f80000) +#define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_MSB _u(23) +#define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_LSB _u(19) +#define PROC_PIO_SM2_EXECCTRL_OUT_EN_SEL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN +// Description : If 1, use a bit of OUT data as an auxiliary write enable +// When used in conjunction with OUT_STICKY, writes with an enable +// of 0 will +// deassert the latest pin write. This can create useful +// masking/override behaviour +// due to the priority ordering of state machine pin writes (SM0 < +// SM1 < ...) +#define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_RESET _u(0x0) +#define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_BITS _u(0x00040000) +#define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_MSB _u(18) +#define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_LSB _u(18) +#define PROC_PIO_SM2_EXECCTRL_INLINE_OUT_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_OUT_STICKY +// Description : Continuously assert the most recent OUT/SET to the pins +#define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_RESET _u(0x0) +#define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_BITS _u(0x00020000) +#define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_MSB _u(17) +#define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_LSB _u(17) +#define PROC_PIO_SM2_EXECCTRL_OUT_STICKY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_WRAP_TOP +// Description : After reaching this address, execution is wrapped to +// wrap_bottom. +// If the instruction is a jump, and the jump condition is true, +// the jump takes priority. +#define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_RESET _u(0x1f) +#define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_BITS _u(0x0001f000) +#define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_MSB _u(16) +#define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_LSB _u(12) +#define PROC_PIO_SM2_EXECCTRL_WRAP_TOP_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM +// Description : After reaching wrap_top, execution is wrapped to this address. +#define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_RESET _u(0x00) +#define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_BITS _u(0x00000f80) +#define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_MSB _u(11) +#define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_LSB _u(7) +#define PROC_PIO_SM2_EXECCTRL_WRAP_BOTTOM_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_STATUS_SEL +// Description : Comparison used for the MOV x, STATUS instruction. +// 0x0 -> All-ones if TX FIFO level < N, otherwise all-zeroes +// 0x1 -> All-ones if RX FIFO level < N, otherwise all-zeroes +#define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_RESET _u(0x0) +#define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_BITS _u(0x00000020) +#define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_MSB _u(5) +#define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_LSB _u(5) +#define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_ACCESS "RW" +#define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_VALUE_TXLEVEL _u(0x0) +#define PROC_PIO_SM2_EXECCTRL_STATUS_SEL_VALUE_RXLEVEL _u(0x1) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_EXECCTRL_STATUS_N +// Description : Comparison level for the MOV x, STATUS instruction +#define PROC_PIO_SM2_EXECCTRL_STATUS_N_RESET _u(0x00) +#define PROC_PIO_SM2_EXECCTRL_STATUS_N_BITS _u(0x0000001f) +#define PROC_PIO_SM2_EXECCTRL_STATUS_N_MSB _u(4) +#define PROC_PIO_SM2_EXECCTRL_STATUS_N_LSB _u(0) +#define PROC_PIO_SM2_EXECCTRL_STATUS_N_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM2_SHIFTCTRL +// Description : Control behaviour of the input/output shift registers for state +// machine 2 +#define PROC_PIO_SM2_SHIFTCTRL_OFFSET _u(0x00000114) +#define PROC_PIO_SM2_SHIFTCTRL_BITS _u(0xffff0000) +#define PROC_PIO_SM2_SHIFTCTRL_RESET _u(0x000c0000) +#define PROC_PIO_SM2_SHIFTCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX +// Description : When 1, RX FIFO steals the TX FIFO's storage, and becomes twice +// as deep. +// TX FIFO is disabled as a result (always reads as both full and +// empty). +// FIFOs are flushed when this bit is changed. +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_RESET _u(0x0) +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_BITS _u(0x80000000) +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_MSB _u(31) +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_LSB _u(31) +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_RX_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX +// Description : When 1, TX FIFO steals the RX FIFO's storage, and becomes twice +// as deep. +// RX FIFO is disabled as a result (always reads as both full and +// empty). +// FIFOs are flushed when this bit is changed. +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_RESET _u(0x0) +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_BITS _u(0x40000000) +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_MSB _u(30) +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_LSB _u(30) +#define PROC_PIO_SM2_SHIFTCTRL_FJOIN_TX_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH +// Description : Number of bits shifted out of TXSR before autopull or +// conditional pull. +// Write 0 for value of 32. +#define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_RESET _u(0x00) +#define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_BITS _u(0x3e000000) +#define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_MSB _u(29) +#define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_LSB _u(25) +#define PROC_PIO_SM2_SHIFTCTRL_PULL_THRESH_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH +// Description : Number of bits shifted into RXSR before autopush or conditional +// push. +// Write 0 for value of 32. +#define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_RESET _u(0x00) +#define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_BITS _u(0x01f00000) +#define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_MSB _u(24) +#define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_LSB _u(20) +#define PROC_PIO_SM2_SHIFTCTRL_PUSH_THRESH_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR +// Description : 1 = shift out of output shift register to right. 0 = to left. +#define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_RESET _u(0x1) +#define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_BITS _u(0x00080000) +#define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_MSB _u(19) +#define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_LSB _u(19) +#define PROC_PIO_SM2_SHIFTCTRL_OUT_SHIFTDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR +// Description : 1 = shift input shift register to right (data enters from +// left). 0 = to left. +#define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_RESET _u(0x1) +#define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_BITS _u(0x00040000) +#define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_MSB _u(18) +#define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_LSB _u(18) +#define PROC_PIO_SM2_SHIFTCTRL_IN_SHIFTDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_SHIFTCTRL_AUTOPULL +// Description : Pull automatically when the output shift register is emptied +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_RESET _u(0x0) +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_BITS _u(0x00020000) +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_MSB _u(17) +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_LSB _u(17) +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH +// Description : Push automatically when the input shift register is filled +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_RESET _u(0x0) +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_BITS _u(0x00010000) +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_MSB _u(16) +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_LSB _u(16) +#define PROC_PIO_SM2_SHIFTCTRL_AUTOPUSH_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM2_ADDR +// Description : Current instruction address of state machine 2 +#define PROC_PIO_SM2_ADDR_OFFSET _u(0x00000118) +#define PROC_PIO_SM2_ADDR_BITS _u(0x0000001f) +#define PROC_PIO_SM2_ADDR_RESET _u(0x00000000) +#define PROC_PIO_SM2_ADDR_WIDTH _u(32) +#define PROC_PIO_SM2_ADDR_MSB _u(4) +#define PROC_PIO_SM2_ADDR_LSB _u(0) +#define PROC_PIO_SM2_ADDR_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_SM2_INSTR +// Description : Instruction currently being executed by state machine 2 +// Write to execute an instruction immediately (including jumps) +// and then resume execution. +#define PROC_PIO_SM2_INSTR_OFFSET _u(0x0000011c) +#define PROC_PIO_SM2_INSTR_BITS _u(0x0000ffff) +#define PROC_PIO_SM2_INSTR_RESET "-" +#define PROC_PIO_SM2_INSTR_WIDTH _u(32) +#define PROC_PIO_SM2_INSTR_MSB _u(15) +#define PROC_PIO_SM2_INSTR_LSB _u(0) +#define PROC_PIO_SM2_INSTR_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM2_PINCTRL +// Description : State machine pin control +#define PROC_PIO_SM2_PINCTRL_OFFSET _u(0x00000120) +#define PROC_PIO_SM2_PINCTRL_BITS _u(0xffffffff) +#define PROC_PIO_SM2_PINCTRL_RESET _u(0x14000000) +#define PROC_PIO_SM2_PINCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_PINCTRL_SIDESET_COUNT +// Description : The number of delay bits co-opted for side-set. Inclusive of +// the enable bit, if present. +#define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_RESET _u(0x0) +#define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_BITS _u(0xe0000000) +#define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_MSB _u(31) +#define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_LSB _u(29) +#define PROC_PIO_SM2_PINCTRL_SIDESET_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_PINCTRL_SET_COUNT +// Description : The number of pins asserted by a SET. Max of 5 +#define PROC_PIO_SM2_PINCTRL_SET_COUNT_RESET _u(0x5) +#define PROC_PIO_SM2_PINCTRL_SET_COUNT_BITS _u(0x1c000000) +#define PROC_PIO_SM2_PINCTRL_SET_COUNT_MSB _u(28) +#define PROC_PIO_SM2_PINCTRL_SET_COUNT_LSB _u(26) +#define PROC_PIO_SM2_PINCTRL_SET_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_PINCTRL_OUT_COUNT +// Description : The number of pins asserted by an OUT. Value of 0 -> 32 pins +#define PROC_PIO_SM2_PINCTRL_OUT_COUNT_RESET _u(0x00) +#define PROC_PIO_SM2_PINCTRL_OUT_COUNT_BITS _u(0x03f00000) +#define PROC_PIO_SM2_PINCTRL_OUT_COUNT_MSB _u(25) +#define PROC_PIO_SM2_PINCTRL_OUT_COUNT_LSB _u(20) +#define PROC_PIO_SM2_PINCTRL_OUT_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_PINCTRL_IN_BASE +// Description : The virtual pin corresponding to IN bit 0 +#define PROC_PIO_SM2_PINCTRL_IN_BASE_RESET _u(0x00) +#define PROC_PIO_SM2_PINCTRL_IN_BASE_BITS _u(0x000f8000) +#define PROC_PIO_SM2_PINCTRL_IN_BASE_MSB _u(19) +#define PROC_PIO_SM2_PINCTRL_IN_BASE_LSB _u(15) +#define PROC_PIO_SM2_PINCTRL_IN_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_PINCTRL_SIDESET_BASE +// Description : The virtual pin corresponding to delay field bit 0 +#define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_RESET _u(0x00) +#define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_BITS _u(0x00007c00) +#define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_MSB _u(14) +#define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_LSB _u(10) +#define PROC_PIO_SM2_PINCTRL_SIDESET_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_PINCTRL_SET_BASE +// Description : The virtual pin corresponding to SET bit 0 +#define PROC_PIO_SM2_PINCTRL_SET_BASE_RESET _u(0x00) +#define PROC_PIO_SM2_PINCTRL_SET_BASE_BITS _u(0x000003e0) +#define PROC_PIO_SM2_PINCTRL_SET_BASE_MSB _u(9) +#define PROC_PIO_SM2_PINCTRL_SET_BASE_LSB _u(5) +#define PROC_PIO_SM2_PINCTRL_SET_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_PINCTRL_OUT_BASE +// Description : The virtual pin corresponding to OUT bit 0 +#define PROC_PIO_SM2_PINCTRL_OUT_BASE_RESET _u(0x00) +#define PROC_PIO_SM2_PINCTRL_OUT_BASE_BITS _u(0x0000001f) +#define PROC_PIO_SM2_PINCTRL_OUT_BASE_MSB _u(4) +#define PROC_PIO_SM2_PINCTRL_OUT_BASE_LSB _u(0) +#define PROC_PIO_SM2_PINCTRL_OUT_BASE_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM2_DMACTRL_TX +// Description : State machine DMA control +#define PROC_PIO_SM2_DMACTRL_TX_OFFSET _u(0x00000124) +#define PROC_PIO_SM2_DMACTRL_TX_BITS _u(0xc0000f9f) +#define PROC_PIO_SM2_DMACTRL_TX_RESET _u(0x00000104) +#define PROC_PIO_SM2_DMACTRL_TX_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_DMACTRL_TX_DREQ_EN +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_RESET _u(0x0) +#define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_BITS _u(0x80000000) +#define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_MSB _u(31) +#define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_LSB _u(31) +#define PROC_PIO_SM2_DMACTRL_TX_DREQ_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_DMACTRL_TX_ACTIVE +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_RESET "-" +#define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_BITS _u(0x40000000) +#define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_MSB _u(30) +#define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_LSB _u(30) +#define PROC_PIO_SM2_DMACTRL_TX_ACTIVE_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME +// Description : Delay in number of bus cycles before successive DREQs are +// generated. +// Used to account for system bus latency in write data arriving +// at the FIFO. +#define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_RESET _u(0x02) +#define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_BITS _u(0x00000f80) +#define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_MSB _u(11) +#define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_LSB _u(7) +#define PROC_PIO_SM2_DMACTRL_TX_DWELL_TIME_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD +// Description : Threshold control. If there are no more than THRESHOLD items in +// the TX FIFO, DMA dreq and/or the interrupt line is asserted. +#define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_RESET _u(0x04) +#define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_BITS _u(0x0000001f) +#define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_MSB _u(4) +#define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_LSB _u(0) +#define PROC_PIO_SM2_DMACTRL_TX_FIFO_THRESHOLD_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM2_DMACTRL_RX +// Description : State machine DMA control +#define PROC_PIO_SM2_DMACTRL_RX_OFFSET _u(0x00000128) +#define PROC_PIO_SM2_DMACTRL_RX_BITS _u(0xc0000f9f) +#define PROC_PIO_SM2_DMACTRL_RX_RESET _u(0x00000104) +#define PROC_PIO_SM2_DMACTRL_RX_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_DMACTRL_RX_DREQ_EN +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_RESET _u(0x0) +#define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_BITS _u(0x80000000) +#define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_MSB _u(31) +#define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_LSB _u(31) +#define PROC_PIO_SM2_DMACTRL_RX_DREQ_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_DMACTRL_RX_ACTIVE +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_RESET "-" +#define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_BITS _u(0x40000000) +#define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_MSB _u(30) +#define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_LSB _u(30) +#define PROC_PIO_SM2_DMACTRL_RX_ACTIVE_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME +// Description : Delay in number of bus cycles before successive DREQs are +// generated. +// Used to account for system bus latency in write data arriving +// at the FIFO. +#define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_RESET _u(0x02) +#define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_BITS _u(0x00000f80) +#define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_MSB _u(11) +#define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_LSB _u(7) +#define PROC_PIO_SM2_DMACTRL_RX_DWELL_TIME_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD +// Description : Threshold control. If there are at least THRESHOLD items in the +// RX FIFO, DMA dreq and/or the interrupt line is asserted. +#define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_RESET _u(0x04) +#define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_BITS _u(0x0000001f) +#define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_MSB _u(4) +#define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_LSB _u(0) +#define PROC_PIO_SM2_DMACTRL_RX_FIFO_THRESHOLD_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM3_CLKDIV +// Description : Clock divider register for state machine 3 +// Frequency = clock freq / (CLKDIV_INT + CLKDIV_FRAC / 256) +#define PROC_PIO_SM3_CLKDIV_OFFSET _u(0x0000012c) +#define PROC_PIO_SM3_CLKDIV_BITS _u(0xffffff00) +#define PROC_PIO_SM3_CLKDIV_RESET _u(0x00010000) +#define PROC_PIO_SM3_CLKDIV_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_CLKDIV_INT +// Description : Effective frequency is sysclk/int. +// Value of 0 is interpreted as max possible value +#define PROC_PIO_SM3_CLKDIV_INT_RESET _u(0x0001) +#define PROC_PIO_SM3_CLKDIV_INT_BITS _u(0xffff0000) +#define PROC_PIO_SM3_CLKDIV_INT_MSB _u(31) +#define PROC_PIO_SM3_CLKDIV_INT_LSB _u(16) +#define PROC_PIO_SM3_CLKDIV_INT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_CLKDIV_FRAC +// Description : Fractional part of clock divider +#define PROC_PIO_SM3_CLKDIV_FRAC_RESET _u(0x00) +#define PROC_PIO_SM3_CLKDIV_FRAC_BITS _u(0x0000ff00) +#define PROC_PIO_SM3_CLKDIV_FRAC_MSB _u(15) +#define PROC_PIO_SM3_CLKDIV_FRAC_LSB _u(8) +#define PROC_PIO_SM3_CLKDIV_FRAC_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM3_EXECCTRL +// Description : Execution/behavioural settings for state machine 3 +#define PROC_PIO_SM3_EXECCTRL_OFFSET _u(0x00000130) +#define PROC_PIO_SM3_EXECCTRL_BITS _u(0xffffffbf) +#define PROC_PIO_SM3_EXECCTRL_RESET _u(0x0001f000) +#define PROC_PIO_SM3_EXECCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_EXEC_STALLED +// Description : An instruction written to SMx_INSTR is stalled, and latched by +// the +// state machine. Will clear once the instruction completes. +#define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_RESET _u(0x0) +#define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_BITS _u(0x80000000) +#define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_MSB _u(31) +#define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_LSB _u(31) +#define PROC_PIO_SM3_EXECCTRL_EXEC_STALLED_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_SIDE_EN +// Description : If 1, the delay MSB is used as side-set enable, rather than a +// side-set data bit. This allows instructions to perform side-set +// optionally, +// rather than on every instruction. +#define PROC_PIO_SM3_EXECCTRL_SIDE_EN_RESET _u(0x0) +#define PROC_PIO_SM3_EXECCTRL_SIDE_EN_BITS _u(0x40000000) +#define PROC_PIO_SM3_EXECCTRL_SIDE_EN_MSB _u(30) +#define PROC_PIO_SM3_EXECCTRL_SIDE_EN_LSB _u(30) +#define PROC_PIO_SM3_EXECCTRL_SIDE_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR +// Description : Side-set data is asserted to pin OEs instead of pin values +#define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_RESET _u(0x0) +#define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_BITS _u(0x20000000) +#define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_MSB _u(29) +#define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_LSB _u(29) +#define PROC_PIO_SM3_EXECCTRL_SIDE_PINDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_JMP_PIN +// Description : The GPIO number to use as condition for JMP PIN. Unaffected by +// input mapping. +#define PROC_PIO_SM3_EXECCTRL_JMP_PIN_RESET _u(0x00) +#define PROC_PIO_SM3_EXECCTRL_JMP_PIN_BITS _u(0x1f000000) +#define PROC_PIO_SM3_EXECCTRL_JMP_PIN_MSB _u(28) +#define PROC_PIO_SM3_EXECCTRL_JMP_PIN_LSB _u(24) +#define PROC_PIO_SM3_EXECCTRL_JMP_PIN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL +// Description : Which data bit to use for inline OUT enable +#define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_RESET _u(0x00) +#define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_BITS _u(0x00f80000) +#define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_MSB _u(23) +#define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_LSB _u(19) +#define PROC_PIO_SM3_EXECCTRL_OUT_EN_SEL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN +// Description : If 1, use a bit of OUT data as an auxiliary write enable +// When used in conjunction with OUT_STICKY, writes with an enable +// of 0 will +// deassert the latest pin write. This can create useful +// masking/override behaviour +// due to the priority ordering of state machine pin writes (SM0 < +// SM1 < ...) +#define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_RESET _u(0x0) +#define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_BITS _u(0x00040000) +#define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_MSB _u(18) +#define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_LSB _u(18) +#define PROC_PIO_SM3_EXECCTRL_INLINE_OUT_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_OUT_STICKY +// Description : Continuously assert the most recent OUT/SET to the pins +#define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_RESET _u(0x0) +#define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_BITS _u(0x00020000) +#define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_MSB _u(17) +#define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_LSB _u(17) +#define PROC_PIO_SM3_EXECCTRL_OUT_STICKY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_WRAP_TOP +// Description : After reaching this address, execution is wrapped to +// wrap_bottom. +// If the instruction is a jump, and the jump condition is true, +// the jump takes priority. +#define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_RESET _u(0x1f) +#define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_BITS _u(0x0001f000) +#define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_MSB _u(16) +#define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_LSB _u(12) +#define PROC_PIO_SM3_EXECCTRL_WRAP_TOP_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM +// Description : After reaching wrap_top, execution is wrapped to this address. +#define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_RESET _u(0x00) +#define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_BITS _u(0x00000f80) +#define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_MSB _u(11) +#define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_LSB _u(7) +#define PROC_PIO_SM3_EXECCTRL_WRAP_BOTTOM_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_STATUS_SEL +// Description : Comparison used for the MOV x, STATUS instruction. +// 0x0 -> All-ones if TX FIFO level < N, otherwise all-zeroes +// 0x1 -> All-ones if RX FIFO level < N, otherwise all-zeroes +#define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_RESET _u(0x0) +#define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_BITS _u(0x00000020) +#define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_MSB _u(5) +#define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_LSB _u(5) +#define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_ACCESS "RW" +#define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_VALUE_TXLEVEL _u(0x0) +#define PROC_PIO_SM3_EXECCTRL_STATUS_SEL_VALUE_RXLEVEL _u(0x1) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_EXECCTRL_STATUS_N +// Description : Comparison level for the MOV x, STATUS instruction +#define PROC_PIO_SM3_EXECCTRL_STATUS_N_RESET _u(0x00) +#define PROC_PIO_SM3_EXECCTRL_STATUS_N_BITS _u(0x0000001f) +#define PROC_PIO_SM3_EXECCTRL_STATUS_N_MSB _u(4) +#define PROC_PIO_SM3_EXECCTRL_STATUS_N_LSB _u(0) +#define PROC_PIO_SM3_EXECCTRL_STATUS_N_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM3_SHIFTCTRL +// Description : Control behaviour of the input/output shift registers for state +// machine 3 +#define PROC_PIO_SM3_SHIFTCTRL_OFFSET _u(0x00000134) +#define PROC_PIO_SM3_SHIFTCTRL_BITS _u(0xffff0000) +#define PROC_PIO_SM3_SHIFTCTRL_RESET _u(0x000c0000) +#define PROC_PIO_SM3_SHIFTCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX +// Description : When 1, RX FIFO steals the TX FIFO's storage, and becomes twice +// as deep. +// TX FIFO is disabled as a result (always reads as both full and +// empty). +// FIFOs are flushed when this bit is changed. +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_RESET _u(0x0) +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_BITS _u(0x80000000) +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_MSB _u(31) +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_LSB _u(31) +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_RX_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX +// Description : When 1, TX FIFO steals the RX FIFO's storage, and becomes twice +// as deep. +// RX FIFO is disabled as a result (always reads as both full and +// empty). +// FIFOs are flushed when this bit is changed. +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_RESET _u(0x0) +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_BITS _u(0x40000000) +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_MSB _u(30) +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_LSB _u(30) +#define PROC_PIO_SM3_SHIFTCTRL_FJOIN_TX_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH +// Description : Number of bits shifted out of TXSR before autopull or +// conditional pull. +// Write 0 for value of 32. +#define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_RESET _u(0x00) +#define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_BITS _u(0x3e000000) +#define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_MSB _u(29) +#define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_LSB _u(25) +#define PROC_PIO_SM3_SHIFTCTRL_PULL_THRESH_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH +// Description : Number of bits shifted into RXSR before autopush or conditional +// push. +// Write 0 for value of 32. +#define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_RESET _u(0x00) +#define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_BITS _u(0x01f00000) +#define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_MSB _u(24) +#define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_LSB _u(20) +#define PROC_PIO_SM3_SHIFTCTRL_PUSH_THRESH_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR +// Description : 1 = shift out of output shift register to right. 0 = to left. +#define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_RESET _u(0x1) +#define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_BITS _u(0x00080000) +#define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_MSB _u(19) +#define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_LSB _u(19) +#define PROC_PIO_SM3_SHIFTCTRL_OUT_SHIFTDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR +// Description : 1 = shift input shift register to right (data enters from +// left). 0 = to left. +#define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_RESET _u(0x1) +#define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_BITS _u(0x00040000) +#define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_MSB _u(18) +#define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_LSB _u(18) +#define PROC_PIO_SM3_SHIFTCTRL_IN_SHIFTDIR_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_SHIFTCTRL_AUTOPULL +// Description : Pull automatically when the output shift register is emptied +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_RESET _u(0x0) +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_BITS _u(0x00020000) +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_MSB _u(17) +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_LSB _u(17) +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH +// Description : Push automatically when the input shift register is filled +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_RESET _u(0x0) +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_BITS _u(0x00010000) +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_MSB _u(16) +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_LSB _u(16) +#define PROC_PIO_SM3_SHIFTCTRL_AUTOPUSH_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM3_ADDR +// Description : Current instruction address of state machine 3 +#define PROC_PIO_SM3_ADDR_OFFSET _u(0x00000138) +#define PROC_PIO_SM3_ADDR_BITS _u(0x0000001f) +#define PROC_PIO_SM3_ADDR_RESET _u(0x00000000) +#define PROC_PIO_SM3_ADDR_WIDTH _u(32) +#define PROC_PIO_SM3_ADDR_MSB _u(4) +#define PROC_PIO_SM3_ADDR_LSB _u(0) +#define PROC_PIO_SM3_ADDR_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_SM3_INSTR +// Description : Instruction currently being executed by state machine 3 +// Write to execute an instruction immediately (including jumps) +// and then resume execution. +#define PROC_PIO_SM3_INSTR_OFFSET _u(0x0000013c) +#define PROC_PIO_SM3_INSTR_BITS _u(0x0000ffff) +#define PROC_PIO_SM3_INSTR_RESET "-" +#define PROC_PIO_SM3_INSTR_WIDTH _u(32) +#define PROC_PIO_SM3_INSTR_MSB _u(15) +#define PROC_PIO_SM3_INSTR_LSB _u(0) +#define PROC_PIO_SM3_INSTR_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM3_PINCTRL +// Description : State machine pin control +#define PROC_PIO_SM3_PINCTRL_OFFSET _u(0x00000140) +#define PROC_PIO_SM3_PINCTRL_BITS _u(0xffffffff) +#define PROC_PIO_SM3_PINCTRL_RESET _u(0x14000000) +#define PROC_PIO_SM3_PINCTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_PINCTRL_SIDESET_COUNT +// Description : The number of delay bits co-opted for side-set. Inclusive of +// the enable bit, if present. +#define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_RESET _u(0x0) +#define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_BITS _u(0xe0000000) +#define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_MSB _u(31) +#define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_LSB _u(29) +#define PROC_PIO_SM3_PINCTRL_SIDESET_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_PINCTRL_SET_COUNT +// Description : The number of pins asserted by a SET. Max of 5 +#define PROC_PIO_SM3_PINCTRL_SET_COUNT_RESET _u(0x5) +#define PROC_PIO_SM3_PINCTRL_SET_COUNT_BITS _u(0x1c000000) +#define PROC_PIO_SM3_PINCTRL_SET_COUNT_MSB _u(28) +#define PROC_PIO_SM3_PINCTRL_SET_COUNT_LSB _u(26) +#define PROC_PIO_SM3_PINCTRL_SET_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_PINCTRL_OUT_COUNT +// Description : The number of pins asserted by an OUT. Value of 0 -> 32 pins +#define PROC_PIO_SM3_PINCTRL_OUT_COUNT_RESET _u(0x00) +#define PROC_PIO_SM3_PINCTRL_OUT_COUNT_BITS _u(0x03f00000) +#define PROC_PIO_SM3_PINCTRL_OUT_COUNT_MSB _u(25) +#define PROC_PIO_SM3_PINCTRL_OUT_COUNT_LSB _u(20) +#define PROC_PIO_SM3_PINCTRL_OUT_COUNT_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_PINCTRL_IN_BASE +// Description : The virtual pin corresponding to IN bit 0 +#define PROC_PIO_SM3_PINCTRL_IN_BASE_RESET _u(0x00) +#define PROC_PIO_SM3_PINCTRL_IN_BASE_BITS _u(0x000f8000) +#define PROC_PIO_SM3_PINCTRL_IN_BASE_MSB _u(19) +#define PROC_PIO_SM3_PINCTRL_IN_BASE_LSB _u(15) +#define PROC_PIO_SM3_PINCTRL_IN_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_PINCTRL_SIDESET_BASE +// Description : The virtual pin corresponding to delay field bit 0 +#define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_RESET _u(0x00) +#define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_BITS _u(0x00007c00) +#define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_MSB _u(14) +#define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_LSB _u(10) +#define PROC_PIO_SM3_PINCTRL_SIDESET_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_PINCTRL_SET_BASE +// Description : The virtual pin corresponding to SET bit 0 +#define PROC_PIO_SM3_PINCTRL_SET_BASE_RESET _u(0x00) +#define PROC_PIO_SM3_PINCTRL_SET_BASE_BITS _u(0x000003e0) +#define PROC_PIO_SM3_PINCTRL_SET_BASE_MSB _u(9) +#define PROC_PIO_SM3_PINCTRL_SET_BASE_LSB _u(5) +#define PROC_PIO_SM3_PINCTRL_SET_BASE_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_PINCTRL_OUT_BASE +// Description : The virtual pin corresponding to OUT bit 0 +#define PROC_PIO_SM3_PINCTRL_OUT_BASE_RESET _u(0x00) +#define PROC_PIO_SM3_PINCTRL_OUT_BASE_BITS _u(0x0000001f) +#define PROC_PIO_SM3_PINCTRL_OUT_BASE_MSB _u(4) +#define PROC_PIO_SM3_PINCTRL_OUT_BASE_LSB _u(0) +#define PROC_PIO_SM3_PINCTRL_OUT_BASE_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM3_DMACTRL_TX +// Description : State machine DMA control +#define PROC_PIO_SM3_DMACTRL_TX_OFFSET _u(0x00000144) +#define PROC_PIO_SM3_DMACTRL_TX_BITS _u(0xc0000f9f) +#define PROC_PIO_SM3_DMACTRL_TX_RESET _u(0x00000104) +#define PROC_PIO_SM3_DMACTRL_TX_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_DMACTRL_TX_DREQ_EN +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_RESET _u(0x0) +#define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_BITS _u(0x80000000) +#define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_MSB _u(31) +#define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_LSB _u(31) +#define PROC_PIO_SM3_DMACTRL_TX_DREQ_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_DMACTRL_TX_ACTIVE +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_RESET "-" +#define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_BITS _u(0x40000000) +#define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_MSB _u(30) +#define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_LSB _u(30) +#define PROC_PIO_SM3_DMACTRL_TX_ACTIVE_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME +// Description : Delay in number of bus cycles before successive DREQs are +// generated. +// Used to account for system bus latency in write data arriving +// at the FIFO. +#define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_RESET _u(0x02) +#define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_BITS _u(0x00000f80) +#define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_MSB _u(11) +#define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_LSB _u(7) +#define PROC_PIO_SM3_DMACTRL_TX_DWELL_TIME_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD +// Description : Threshold control. If there are no more than THRESHOLD items in +// the TX FIFO, DMA dreq and/or the interrupt line is asserted. +#define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_RESET _u(0x04) +#define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_BITS _u(0x0000001f) +#define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_MSB _u(4) +#define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_LSB _u(0) +#define PROC_PIO_SM3_DMACTRL_TX_FIFO_THRESHOLD_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_SM3_DMACTRL_RX +// Description : State machine DMA control +#define PROC_PIO_SM3_DMACTRL_RX_OFFSET _u(0x00000148) +#define PROC_PIO_SM3_DMACTRL_RX_BITS _u(0xc0000f9f) +#define PROC_PIO_SM3_DMACTRL_RX_RESET _u(0x00000104) +#define PROC_PIO_SM3_DMACTRL_RX_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_DMACTRL_RX_DREQ_EN +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_RESET _u(0x0) +#define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_BITS _u(0x80000000) +#define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_MSB _u(31) +#define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_LSB _u(31) +#define PROC_PIO_SM3_DMACTRL_RX_DREQ_EN_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_DMACTRL_RX_ACTIVE +// Description : 1 - Assert DREQ to DMA when fewer than fifo_threshold spaces +// are available +// 0 - Don't assert DREQ +#define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_RESET "-" +#define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_BITS _u(0x40000000) +#define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_MSB _u(30) +#define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_LSB _u(30) +#define PROC_PIO_SM3_DMACTRL_RX_ACTIVE_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME +// Description : Delay in number of bus cycles before successive DREQs are +// generated. +// Used to account for system bus latency in write data arriving +// at the FIFO. +#define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_RESET _u(0x02) +#define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_BITS _u(0x00000f80) +#define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_MSB _u(11) +#define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_LSB _u(7) +#define PROC_PIO_SM3_DMACTRL_RX_DWELL_TIME_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD +// Description : Threshold control. If there are at least THRESHOLD items in the +// RX FIFO, DMA dreq and/or the interrupt line is asserted. +#define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_RESET _u(0x04) +#define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_BITS _u(0x0000001f) +#define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_MSB _u(4) +#define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_LSB _u(0) +#define PROC_PIO_SM3_DMACTRL_RX_FIFO_THRESHOLD_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_INTR +// Description : Raw Interrupts +#define PROC_PIO_INTR_OFFSET _u(0x0000014c) +#define PROC_PIO_INTR_BITS _u(0x00000fff) +#define PROC_PIO_INTR_RESET _u(0x00000000) +#define PROC_PIO_INTR_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM3 +// Description : None +#define PROC_PIO_INTR_SM3_RESET _u(0x0) +#define PROC_PIO_INTR_SM3_BITS _u(0x00000800) +#define PROC_PIO_INTR_SM3_MSB _u(11) +#define PROC_PIO_INTR_SM3_LSB _u(11) +#define PROC_PIO_INTR_SM3_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM2 +// Description : None +#define PROC_PIO_INTR_SM2_RESET _u(0x0) +#define PROC_PIO_INTR_SM2_BITS _u(0x00000400) +#define PROC_PIO_INTR_SM2_MSB _u(10) +#define PROC_PIO_INTR_SM2_LSB _u(10) +#define PROC_PIO_INTR_SM2_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM1 +// Description : None +#define PROC_PIO_INTR_SM1_RESET _u(0x0) +#define PROC_PIO_INTR_SM1_BITS _u(0x00000200) +#define PROC_PIO_INTR_SM1_MSB _u(9) +#define PROC_PIO_INTR_SM1_LSB _u(9) +#define PROC_PIO_INTR_SM1_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM0 +// Description : None +#define PROC_PIO_INTR_SM0_RESET _u(0x0) +#define PROC_PIO_INTR_SM0_BITS _u(0x00000100) +#define PROC_PIO_INTR_SM0_MSB _u(8) +#define PROC_PIO_INTR_SM0_LSB _u(8) +#define PROC_PIO_INTR_SM0_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM3_TXNFULL +// Description : None +#define PROC_PIO_INTR_SM3_TXNFULL_RESET _u(0x0) +#define PROC_PIO_INTR_SM3_TXNFULL_BITS _u(0x00000080) +#define PROC_PIO_INTR_SM3_TXNFULL_MSB _u(7) +#define PROC_PIO_INTR_SM3_TXNFULL_LSB _u(7) +#define PROC_PIO_INTR_SM3_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM2_TXNFULL +// Description : None +#define PROC_PIO_INTR_SM2_TXNFULL_RESET _u(0x0) +#define PROC_PIO_INTR_SM2_TXNFULL_BITS _u(0x00000040) +#define PROC_PIO_INTR_SM2_TXNFULL_MSB _u(6) +#define PROC_PIO_INTR_SM2_TXNFULL_LSB _u(6) +#define PROC_PIO_INTR_SM2_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM1_TXNFULL +// Description : None +#define PROC_PIO_INTR_SM1_TXNFULL_RESET _u(0x0) +#define PROC_PIO_INTR_SM1_TXNFULL_BITS _u(0x00000020) +#define PROC_PIO_INTR_SM1_TXNFULL_MSB _u(5) +#define PROC_PIO_INTR_SM1_TXNFULL_LSB _u(5) +#define PROC_PIO_INTR_SM1_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM0_TXNFULL +// Description : None +#define PROC_PIO_INTR_SM0_TXNFULL_RESET _u(0x0) +#define PROC_PIO_INTR_SM0_TXNFULL_BITS _u(0x00000010) +#define PROC_PIO_INTR_SM0_TXNFULL_MSB _u(4) +#define PROC_PIO_INTR_SM0_TXNFULL_LSB _u(4) +#define PROC_PIO_INTR_SM0_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM3_RXNEMPTY +// Description : None +#define PROC_PIO_INTR_SM3_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_INTR_SM3_RXNEMPTY_BITS _u(0x00000008) +#define PROC_PIO_INTR_SM3_RXNEMPTY_MSB _u(3) +#define PROC_PIO_INTR_SM3_RXNEMPTY_LSB _u(3) +#define PROC_PIO_INTR_SM3_RXNEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM2_RXNEMPTY +// Description : None +#define PROC_PIO_INTR_SM2_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_INTR_SM2_RXNEMPTY_BITS _u(0x00000004) +#define PROC_PIO_INTR_SM2_RXNEMPTY_MSB _u(2) +#define PROC_PIO_INTR_SM2_RXNEMPTY_LSB _u(2) +#define PROC_PIO_INTR_SM2_RXNEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM1_RXNEMPTY +// Description : None +#define PROC_PIO_INTR_SM1_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_INTR_SM1_RXNEMPTY_BITS _u(0x00000002) +#define PROC_PIO_INTR_SM1_RXNEMPTY_MSB _u(1) +#define PROC_PIO_INTR_SM1_RXNEMPTY_LSB _u(1) +#define PROC_PIO_INTR_SM1_RXNEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_INTR_SM0_RXNEMPTY +// Description : None +#define PROC_PIO_INTR_SM0_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_INTR_SM0_RXNEMPTY_BITS _u(0x00000001) +#define PROC_PIO_INTR_SM0_RXNEMPTY_MSB _u(0) +#define PROC_PIO_INTR_SM0_RXNEMPTY_LSB _u(0) +#define PROC_PIO_INTR_SM0_RXNEMPTY_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_IRQ0_INTE +// Description : Interrupt Enable for irq0 +#define PROC_PIO_IRQ0_INTE_OFFSET _u(0x00000150) +#define PROC_PIO_IRQ0_INTE_BITS _u(0x00000fff) +#define PROC_PIO_IRQ0_INTE_RESET _u(0x00000000) +#define PROC_PIO_IRQ0_INTE_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM3 +// Description : None +#define PROC_PIO_IRQ0_INTE_SM3_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM3_BITS _u(0x00000800) +#define PROC_PIO_IRQ0_INTE_SM3_MSB _u(11) +#define PROC_PIO_IRQ0_INTE_SM3_LSB _u(11) +#define PROC_PIO_IRQ0_INTE_SM3_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM2 +// Description : None +#define PROC_PIO_IRQ0_INTE_SM2_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM2_BITS _u(0x00000400) +#define PROC_PIO_IRQ0_INTE_SM2_MSB _u(10) +#define PROC_PIO_IRQ0_INTE_SM2_LSB _u(10) +#define PROC_PIO_IRQ0_INTE_SM2_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM1 +// Description : None +#define PROC_PIO_IRQ0_INTE_SM1_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM1_BITS _u(0x00000200) +#define PROC_PIO_IRQ0_INTE_SM1_MSB _u(9) +#define PROC_PIO_IRQ0_INTE_SM1_LSB _u(9) +#define PROC_PIO_IRQ0_INTE_SM1_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM0 +// Description : None +#define PROC_PIO_IRQ0_INTE_SM0_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM0_BITS _u(0x00000100) +#define PROC_PIO_IRQ0_INTE_SM0_MSB _u(8) +#define PROC_PIO_IRQ0_INTE_SM0_LSB _u(8) +#define PROC_PIO_IRQ0_INTE_SM0_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM3_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_BITS _u(0x00000080) +#define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_MSB _u(7) +#define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_LSB _u(7) +#define PROC_PIO_IRQ0_INTE_SM3_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM2_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_BITS _u(0x00000040) +#define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_MSB _u(6) +#define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_LSB _u(6) +#define PROC_PIO_IRQ0_INTE_SM2_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM1_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_BITS _u(0x00000020) +#define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_MSB _u(5) +#define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_LSB _u(5) +#define PROC_PIO_IRQ0_INTE_SM1_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM0_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_BITS _u(0x00000010) +#define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_MSB _u(4) +#define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_LSB _u(4) +#define PROC_PIO_IRQ0_INTE_SM0_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_BITS _u(0x00000008) +#define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_MSB _u(3) +#define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_LSB _u(3) +#define PROC_PIO_IRQ0_INTE_SM3_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_BITS _u(0x00000004) +#define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_MSB _u(2) +#define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_LSB _u(2) +#define PROC_PIO_IRQ0_INTE_SM2_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_BITS _u(0x00000002) +#define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_MSB _u(1) +#define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_LSB _u(1) +#define PROC_PIO_IRQ0_INTE_SM1_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_BITS _u(0x00000001) +#define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_MSB _u(0) +#define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_LSB _u(0) +#define PROC_PIO_IRQ0_INTE_SM0_RXNEMPTY_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_IRQ0_INTF +// Description : Interrupt Force for irq0 +#define PROC_PIO_IRQ0_INTF_OFFSET _u(0x00000154) +#define PROC_PIO_IRQ0_INTF_BITS _u(0x00000fff) +#define PROC_PIO_IRQ0_INTF_RESET _u(0x00000000) +#define PROC_PIO_IRQ0_INTF_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM3 +// Description : None +#define PROC_PIO_IRQ0_INTF_SM3_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM3_BITS _u(0x00000800) +#define PROC_PIO_IRQ0_INTF_SM3_MSB _u(11) +#define PROC_PIO_IRQ0_INTF_SM3_LSB _u(11) +#define PROC_PIO_IRQ0_INTF_SM3_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM2 +// Description : None +#define PROC_PIO_IRQ0_INTF_SM2_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM2_BITS _u(0x00000400) +#define PROC_PIO_IRQ0_INTF_SM2_MSB _u(10) +#define PROC_PIO_IRQ0_INTF_SM2_LSB _u(10) +#define PROC_PIO_IRQ0_INTF_SM2_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM1 +// Description : None +#define PROC_PIO_IRQ0_INTF_SM1_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM1_BITS _u(0x00000200) +#define PROC_PIO_IRQ0_INTF_SM1_MSB _u(9) +#define PROC_PIO_IRQ0_INTF_SM1_LSB _u(9) +#define PROC_PIO_IRQ0_INTF_SM1_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM0 +// Description : None +#define PROC_PIO_IRQ0_INTF_SM0_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM0_BITS _u(0x00000100) +#define PROC_PIO_IRQ0_INTF_SM0_MSB _u(8) +#define PROC_PIO_IRQ0_INTF_SM0_LSB _u(8) +#define PROC_PIO_IRQ0_INTF_SM0_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM3_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_BITS _u(0x00000080) +#define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_MSB _u(7) +#define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_LSB _u(7) +#define PROC_PIO_IRQ0_INTF_SM3_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM2_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_BITS _u(0x00000040) +#define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_MSB _u(6) +#define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_LSB _u(6) +#define PROC_PIO_IRQ0_INTF_SM2_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM1_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_BITS _u(0x00000020) +#define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_MSB _u(5) +#define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_LSB _u(5) +#define PROC_PIO_IRQ0_INTF_SM1_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM0_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_BITS _u(0x00000010) +#define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_MSB _u(4) +#define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_LSB _u(4) +#define PROC_PIO_IRQ0_INTF_SM0_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_BITS _u(0x00000008) +#define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_MSB _u(3) +#define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_LSB _u(3) +#define PROC_PIO_IRQ0_INTF_SM3_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_BITS _u(0x00000004) +#define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_MSB _u(2) +#define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_LSB _u(2) +#define PROC_PIO_IRQ0_INTF_SM2_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_BITS _u(0x00000002) +#define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_MSB _u(1) +#define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_LSB _u(1) +#define PROC_PIO_IRQ0_INTF_SM1_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS _u(0x00000001) +#define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_MSB _u(0) +#define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_LSB _u(0) +#define PROC_PIO_IRQ0_INTF_SM0_RXNEMPTY_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_IRQ0_INTS +// Description : Interrupt status after masking & forcing for irq0 +#define PROC_PIO_IRQ0_INTS_OFFSET _u(0x00000158) +#define PROC_PIO_IRQ0_INTS_BITS _u(0x00000fff) +#define PROC_PIO_IRQ0_INTS_RESET _u(0x00000000) +#define PROC_PIO_IRQ0_INTS_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM3 +// Description : None +#define PROC_PIO_IRQ0_INTS_SM3_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM3_BITS _u(0x00000800) +#define PROC_PIO_IRQ0_INTS_SM3_MSB _u(11) +#define PROC_PIO_IRQ0_INTS_SM3_LSB _u(11) +#define PROC_PIO_IRQ0_INTS_SM3_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM2 +// Description : None +#define PROC_PIO_IRQ0_INTS_SM2_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM2_BITS _u(0x00000400) +#define PROC_PIO_IRQ0_INTS_SM2_MSB _u(10) +#define PROC_PIO_IRQ0_INTS_SM2_LSB _u(10) +#define PROC_PIO_IRQ0_INTS_SM2_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM1 +// Description : None +#define PROC_PIO_IRQ0_INTS_SM1_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM1_BITS _u(0x00000200) +#define PROC_PIO_IRQ0_INTS_SM1_MSB _u(9) +#define PROC_PIO_IRQ0_INTS_SM1_LSB _u(9) +#define PROC_PIO_IRQ0_INTS_SM1_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM0 +// Description : None +#define PROC_PIO_IRQ0_INTS_SM0_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM0_BITS _u(0x00000100) +#define PROC_PIO_IRQ0_INTS_SM0_MSB _u(8) +#define PROC_PIO_IRQ0_INTS_SM0_LSB _u(8) +#define PROC_PIO_IRQ0_INTS_SM0_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM3_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_BITS _u(0x00000080) +#define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_MSB _u(7) +#define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_LSB _u(7) +#define PROC_PIO_IRQ0_INTS_SM3_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM2_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_BITS _u(0x00000040) +#define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_MSB _u(6) +#define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_LSB _u(6) +#define PROC_PIO_IRQ0_INTS_SM2_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM1_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_BITS _u(0x00000020) +#define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_MSB _u(5) +#define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_LSB _u(5) +#define PROC_PIO_IRQ0_INTS_SM1_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM0_TXNFULL +// Description : None +#define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_BITS _u(0x00000010) +#define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_MSB _u(4) +#define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_LSB _u(4) +#define PROC_PIO_IRQ0_INTS_SM0_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_BITS _u(0x00000008) +#define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_MSB _u(3) +#define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_LSB _u(3) +#define PROC_PIO_IRQ0_INTS_SM3_RXNEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_BITS _u(0x00000004) +#define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_MSB _u(2) +#define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_LSB _u(2) +#define PROC_PIO_IRQ0_INTS_SM2_RXNEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_BITS _u(0x00000002) +#define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_MSB _u(1) +#define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_LSB _u(1) +#define PROC_PIO_IRQ0_INTS_SM1_RXNEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_BITS _u(0x00000001) +#define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_MSB _u(0) +#define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_LSB _u(0) +#define PROC_PIO_IRQ0_INTS_SM0_RXNEMPTY_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_IRQ1_INTE +// Description : Interrupt Enable for irq1 +#define PROC_PIO_IRQ1_INTE_OFFSET _u(0x0000015c) +#define PROC_PIO_IRQ1_INTE_BITS _u(0x00000fff) +#define PROC_PIO_IRQ1_INTE_RESET _u(0x00000000) +#define PROC_PIO_IRQ1_INTE_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM3 +// Description : None +#define PROC_PIO_IRQ1_INTE_SM3_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM3_BITS _u(0x00000800) +#define PROC_PIO_IRQ1_INTE_SM3_MSB _u(11) +#define PROC_PIO_IRQ1_INTE_SM3_LSB _u(11) +#define PROC_PIO_IRQ1_INTE_SM3_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM2 +// Description : None +#define PROC_PIO_IRQ1_INTE_SM2_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM2_BITS _u(0x00000400) +#define PROC_PIO_IRQ1_INTE_SM2_MSB _u(10) +#define PROC_PIO_IRQ1_INTE_SM2_LSB _u(10) +#define PROC_PIO_IRQ1_INTE_SM2_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM1 +// Description : None +#define PROC_PIO_IRQ1_INTE_SM1_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM1_BITS _u(0x00000200) +#define PROC_PIO_IRQ1_INTE_SM1_MSB _u(9) +#define PROC_PIO_IRQ1_INTE_SM1_LSB _u(9) +#define PROC_PIO_IRQ1_INTE_SM1_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM0 +// Description : None +#define PROC_PIO_IRQ1_INTE_SM0_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM0_BITS _u(0x00000100) +#define PROC_PIO_IRQ1_INTE_SM0_MSB _u(8) +#define PROC_PIO_IRQ1_INTE_SM0_LSB _u(8) +#define PROC_PIO_IRQ1_INTE_SM0_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM3_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_BITS _u(0x00000080) +#define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_MSB _u(7) +#define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_LSB _u(7) +#define PROC_PIO_IRQ1_INTE_SM3_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM2_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_BITS _u(0x00000040) +#define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_MSB _u(6) +#define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_LSB _u(6) +#define PROC_PIO_IRQ1_INTE_SM2_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM1_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_BITS _u(0x00000020) +#define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_MSB _u(5) +#define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_LSB _u(5) +#define PROC_PIO_IRQ1_INTE_SM1_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM0_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_BITS _u(0x00000010) +#define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_MSB _u(4) +#define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_LSB _u(4) +#define PROC_PIO_IRQ1_INTE_SM0_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_BITS _u(0x00000008) +#define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_MSB _u(3) +#define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_LSB _u(3) +#define PROC_PIO_IRQ1_INTE_SM3_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_BITS _u(0x00000004) +#define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_MSB _u(2) +#define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_LSB _u(2) +#define PROC_PIO_IRQ1_INTE_SM2_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_BITS _u(0x00000002) +#define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_MSB _u(1) +#define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_LSB _u(1) +#define PROC_PIO_IRQ1_INTE_SM1_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_BITS _u(0x00000001) +#define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_MSB _u(0) +#define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_LSB _u(0) +#define PROC_PIO_IRQ1_INTE_SM0_RXNEMPTY_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_IRQ1_INTF +// Description : Interrupt Force for irq1 +#define PROC_PIO_IRQ1_INTF_OFFSET _u(0x00000160) +#define PROC_PIO_IRQ1_INTF_BITS _u(0x00000fff) +#define PROC_PIO_IRQ1_INTF_RESET _u(0x00000000) +#define PROC_PIO_IRQ1_INTF_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM3 +// Description : None +#define PROC_PIO_IRQ1_INTF_SM3_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM3_BITS _u(0x00000800) +#define PROC_PIO_IRQ1_INTF_SM3_MSB _u(11) +#define PROC_PIO_IRQ1_INTF_SM3_LSB _u(11) +#define PROC_PIO_IRQ1_INTF_SM3_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM2 +// Description : None +#define PROC_PIO_IRQ1_INTF_SM2_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM2_BITS _u(0x00000400) +#define PROC_PIO_IRQ1_INTF_SM2_MSB _u(10) +#define PROC_PIO_IRQ1_INTF_SM2_LSB _u(10) +#define PROC_PIO_IRQ1_INTF_SM2_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM1 +// Description : None +#define PROC_PIO_IRQ1_INTF_SM1_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM1_BITS _u(0x00000200) +#define PROC_PIO_IRQ1_INTF_SM1_MSB _u(9) +#define PROC_PIO_IRQ1_INTF_SM1_LSB _u(9) +#define PROC_PIO_IRQ1_INTF_SM1_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM0 +// Description : None +#define PROC_PIO_IRQ1_INTF_SM0_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM0_BITS _u(0x00000100) +#define PROC_PIO_IRQ1_INTF_SM0_MSB _u(8) +#define PROC_PIO_IRQ1_INTF_SM0_LSB _u(8) +#define PROC_PIO_IRQ1_INTF_SM0_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM3_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_BITS _u(0x00000080) +#define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_MSB _u(7) +#define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_LSB _u(7) +#define PROC_PIO_IRQ1_INTF_SM3_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM2_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_BITS _u(0x00000040) +#define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_MSB _u(6) +#define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_LSB _u(6) +#define PROC_PIO_IRQ1_INTF_SM2_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM1_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_BITS _u(0x00000020) +#define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_MSB _u(5) +#define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_LSB _u(5) +#define PROC_PIO_IRQ1_INTF_SM1_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM0_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_BITS _u(0x00000010) +#define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_MSB _u(4) +#define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_LSB _u(4) +#define PROC_PIO_IRQ1_INTF_SM0_TXNFULL_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_BITS _u(0x00000008) +#define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_MSB _u(3) +#define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_LSB _u(3) +#define PROC_PIO_IRQ1_INTF_SM3_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_BITS _u(0x00000004) +#define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_MSB _u(2) +#define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_LSB _u(2) +#define PROC_PIO_IRQ1_INTF_SM2_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_BITS _u(0x00000002) +#define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_MSB _u(1) +#define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_LSB _u(1) +#define PROC_PIO_IRQ1_INTF_SM1_RXNEMPTY_ACCESS "RW" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_BITS _u(0x00000001) +#define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_MSB _u(0) +#define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_LSB _u(0) +#define PROC_PIO_IRQ1_INTF_SM0_RXNEMPTY_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_IRQ1_INTS +// Description : Interrupt status after masking & forcing for irq1 +#define PROC_PIO_IRQ1_INTS_OFFSET _u(0x00000164) +#define PROC_PIO_IRQ1_INTS_BITS _u(0x00000fff) +#define PROC_PIO_IRQ1_INTS_RESET _u(0x00000000) +#define PROC_PIO_IRQ1_INTS_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM3 +// Description : None +#define PROC_PIO_IRQ1_INTS_SM3_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM3_BITS _u(0x00000800) +#define PROC_PIO_IRQ1_INTS_SM3_MSB _u(11) +#define PROC_PIO_IRQ1_INTS_SM3_LSB _u(11) +#define PROC_PIO_IRQ1_INTS_SM3_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM2 +// Description : None +#define PROC_PIO_IRQ1_INTS_SM2_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM2_BITS _u(0x00000400) +#define PROC_PIO_IRQ1_INTS_SM2_MSB _u(10) +#define PROC_PIO_IRQ1_INTS_SM2_LSB _u(10) +#define PROC_PIO_IRQ1_INTS_SM2_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM1 +// Description : None +#define PROC_PIO_IRQ1_INTS_SM1_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM1_BITS _u(0x00000200) +#define PROC_PIO_IRQ1_INTS_SM1_MSB _u(9) +#define PROC_PIO_IRQ1_INTS_SM1_LSB _u(9) +#define PROC_PIO_IRQ1_INTS_SM1_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM0 +// Description : None +#define PROC_PIO_IRQ1_INTS_SM0_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM0_BITS _u(0x00000100) +#define PROC_PIO_IRQ1_INTS_SM0_MSB _u(8) +#define PROC_PIO_IRQ1_INTS_SM0_LSB _u(8) +#define PROC_PIO_IRQ1_INTS_SM0_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM3_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_BITS _u(0x00000080) +#define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_MSB _u(7) +#define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_LSB _u(7) +#define PROC_PIO_IRQ1_INTS_SM3_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM2_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_BITS _u(0x00000040) +#define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_MSB _u(6) +#define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_LSB _u(6) +#define PROC_PIO_IRQ1_INTS_SM2_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM1_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_BITS _u(0x00000020) +#define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_MSB _u(5) +#define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_LSB _u(5) +#define PROC_PIO_IRQ1_INTS_SM1_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM0_TXNFULL +// Description : None +#define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_BITS _u(0x00000010) +#define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_MSB _u(4) +#define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_LSB _u(4) +#define PROC_PIO_IRQ1_INTS_SM0_TXNFULL_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_BITS _u(0x00000008) +#define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_MSB _u(3) +#define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_LSB _u(3) +#define PROC_PIO_IRQ1_INTS_SM3_RXNEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_BITS _u(0x00000004) +#define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_MSB _u(2) +#define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_LSB _u(2) +#define PROC_PIO_IRQ1_INTS_SM2_RXNEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_BITS _u(0x00000002) +#define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_MSB _u(1) +#define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_LSB _u(1) +#define PROC_PIO_IRQ1_INTS_SM1_RXNEMPTY_ACCESS "RO" +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY +// Description : None +#define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_RESET _u(0x0) +#define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_BITS _u(0x00000001) +#define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_MSB _u(0) +#define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_LSB _u(0) +#define PROC_PIO_IRQ1_INTS_SM0_RXNEMPTY_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_BLOCK_ID +// Description : Block Identifier +// Hexadecimal representation of "pio2" +#define PROC_PIO_BLOCK_ID_OFFSET _u(0x00000168) +#define PROC_PIO_BLOCK_ID_BITS _u(0xffffffff) +#define PROC_PIO_BLOCK_ID_RESET _u(0x70696f32) +#define PROC_PIO_BLOCK_ID_WIDTH _u(32) +#define PROC_PIO_BLOCK_ID_MSB _u(31) +#define PROC_PIO_BLOCK_ID_LSB _u(0) +#define PROC_PIO_BLOCK_ID_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_INSTANCE_ID +// Description : Block Instance Identifier +#define PROC_PIO_INSTANCE_ID_OFFSET _u(0x0000016c) +#define PROC_PIO_INSTANCE_ID_BITS _u(0x0000000f) +#define PROC_PIO_INSTANCE_ID_RESET _u(0x00000000) +#define PROC_PIO_INSTANCE_ID_WIDTH _u(32) +#define PROC_PIO_INSTANCE_ID_MSB _u(3) +#define PROC_PIO_INSTANCE_ID_LSB _u(0) +#define PROC_PIO_INSTANCE_ID_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_RSTSEQ_AUTO +// Description : None +#define PROC_PIO_RSTSEQ_AUTO_OFFSET _u(0x00000170) +#define PROC_PIO_RSTSEQ_AUTO_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_AUTO_RESET _u(0x00000001) +#define PROC_PIO_RSTSEQ_AUTO_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_RSTSEQ_AUTO_BUSADAPTER +// Description : 1 = reset is controlled by the sequencer +// 0 = reset is controlled by rstseq_ctrl +#define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_RESET _u(0x1) +#define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_MSB _u(0) +#define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_LSB _u(0) +#define PROC_PIO_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_RSTSEQ_PARALLEL +// Description : None +#define PROC_PIO_RSTSEQ_PARALLEL_OFFSET _u(0x00000174) +#define PROC_PIO_RSTSEQ_PARALLEL_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_PARALLEL_RESET _u(0x00000000) +#define PROC_PIO_RSTSEQ_PARALLEL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER +// Description : Is this reset parallel (i.e. not part of the sequence) +#define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_RESET _u(0x0) +#define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_MSB _u(0) +#define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_LSB _u(0) +#define PROC_PIO_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO" +// ============================================================================= +// Register : PROC_PIO_RSTSEQ_CTRL +// Description : None +#define PROC_PIO_RSTSEQ_CTRL_OFFSET _u(0x00000178) +#define PROC_PIO_RSTSEQ_CTRL_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_CTRL_RESET _u(0x00000000) +#define PROC_PIO_RSTSEQ_CTRL_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_RSTSEQ_CTRL_BUSADAPTER +// Description : 1 = keep the reset asserted +// 0 = keep the reset deasserted +// This is ignored if rstseq_auto=1 +#define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_RESET _u(0x0) +#define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_MSB _u(0) +#define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_LSB _u(0) +#define PROC_PIO_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW" +// ============================================================================= +// Register : PROC_PIO_RSTSEQ_TRIG +// Description : None +#define PROC_PIO_RSTSEQ_TRIG_OFFSET _u(0x0000017c) +#define PROC_PIO_RSTSEQ_TRIG_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_TRIG_RESET _u(0x00000000) +#define PROC_PIO_RSTSEQ_TRIG_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_RSTSEQ_TRIG_BUSADAPTER +// Description : Pulses the reset output +#define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_RESET _u(0x0) +#define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_MSB _u(0) +#define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_LSB _u(0) +#define PROC_PIO_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC" +// ============================================================================= +// Register : PROC_PIO_RSTSEQ_DONE +// Description : None +#define PROC_PIO_RSTSEQ_DONE_OFFSET _u(0x00000180) +#define PROC_PIO_RSTSEQ_DONE_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_DONE_RESET _u(0x00000000) +#define PROC_PIO_RSTSEQ_DONE_WIDTH _u(32) +// ----------------------------------------------------------------------------- +// Field : PROC_PIO_RSTSEQ_DONE_BUSADAPTER +// Description : Indicates the current state of the reset +#define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_RESET _u(0x0) +#define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_BITS _u(0x00000001) +#define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_MSB _u(0) +#define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_LSB _u(0) +#define PROC_PIO_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO" +// ============================================================================= +#endif // HARDWARE_REGS_PROC_PIO_DEFINED diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/pio_platform.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/pio_platform.h new file mode 100644 index 000000000000..b8c8b23ed848 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/pio_platform.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Raspberry Pi Ltd. + * All rights reserved. + */ + +#ifndef _PIO_PLATFORM_H +#define _PIO_PLATFORM_H + +#include +#include +#include + +#ifndef __unused +#define __unused __attribute__((unused)) +#endif + +#define PICO_DEFAULT_LED_PIN 4 + +#ifndef PARAM_ASSERTIONS_ENABLE_ALL +#define PARAM_ASSERTIONS_ENABLE_ALL 0 +#endif + +#ifndef PARAM_ASSERTIONS_DISABLE_ALL +#define PARAM_ASSERTIONS_DISABLE_ALL 0 +#endif + +#define PARAM_ASSERTIONS_ENABLED(x) ((PARAM_ASSERTIONS_ENABLED_ ## x || PARAM_ASSERTIONS_ENABLE_ALL) && !PARAM_ASSERTIONS_DISABLE_ALL) +#define invalid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(!(test));}) +#define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(test);}) + +#define STATIC_ASSERT(cond) static_assert(cond, #cond) + +#define _u(x) ((uint)(x)) +#define bool_to_bit(x) ((uint)!!(x)) + +#ifndef count_of +#define count_of(a) (sizeof(a)/sizeof((a)[0])) +#endif + +typedef unsigned int uint; + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/piolib.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/piolib.h new file mode 100644 index 000000000000..0d561f1a1711 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/piolib.h @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023-24 Raspberry Pi Ltd. + * All rights reserved. + */ + +#ifndef _PIOLIB_H +#define _PIOLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "pio_platform.h" +#include "hardware/clocks.h" +#include "hardware/gpio.h" + +#ifndef PARAM_ASSERTIONS_ENABLED_PIO +#define PARAM_ASSERTIONS_ENABLED_PIO 0 +#endif + +#define PIO_ERR(x)((PIO)(uintptr_t)(x)) +#define PIO_IS_ERR(x)(((uintptr_t)(x) >= (uintptr_t)-200)) +#define PIO_ERR_VAL(x)((int)(uintptr_t)(x)) + +#define PIO_ORIGIN_ANY ((uint)(~0)) +#define PIO_ORIGIN_INVALID PIO_ORIGIN_ANY + +#define pio0 pio_open_helper(0) + +enum pio_fifo_join { + PIO_FIFO_JOIN_NONE = 0, + PIO_FIFO_JOIN_TX = 1, + PIO_FIFO_JOIN_RX = 2, +}; + +enum pio_mov_status_type { + STATUS_TX_LESSTHAN = 0, + STATUS_RX_LESSTHAN = 1 +}; + +enum pio_xfer_dir { + PIO_DIR_TO_SM, + PIO_DIR_FROM_SM, + PIO_DIR_COUNT +}; + +#ifndef PIOLIB_INTERNALS + +enum pio_instr_bits { + pio_instr_bits_jmp = 0x0000, + pio_instr_bits_wait = 0x2000, + pio_instr_bits_in = 0x4000, + pio_instr_bits_out = 0x6000, + pio_instr_bits_push = 0x8000, + pio_instr_bits_pull = 0x8080, + pio_instr_bits_mov = 0xa000, + pio_instr_bits_irq = 0xc000, + pio_instr_bits_set = 0xe000, +}; + +#ifndef NDEBUG +#define _PIO_INVALID_IN_SRC 0x08u +#define _PIO_INVALID_OUT_DEST 0x10u +#define _PIO_INVALID_SET_DEST 0x20u +#define _PIO_INVALID_MOV_SRC 0x40u +#define _PIO_INVALID_MOV_DEST 0x80u +#else +#define _PIO_INVALID_IN_SRC 0u +#define _PIO_INVALID_OUT_DEST 0u +#define _PIO_INVALID_SET_DEST 0u +#define _PIO_INVALID_MOV_SRC 0u +#define _PIO_INVALID_MOV_DEST 0u +#endif + +enum pio_src_dest { + pio_pins = 0u, + pio_x = 1u, + pio_y = 2u, + pio_null = 3u | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, + pio_pindirs = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, + pio_exec_mov = 4u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, + pio_status = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_DEST, + pio_pc = 5u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC, + pio_isr = 6u | _PIO_INVALID_SET_DEST, + pio_osr = 7u | _PIO_INVALID_OUT_DEST | _PIO_INVALID_SET_DEST, + pio_exec_out = 7u | _PIO_INVALID_IN_SRC | _PIO_INVALID_SET_DEST | _PIO_INVALID_MOV_SRC | _PIO_INVALID_MOV_DEST, +}; + +#endif + +typedef struct pio_program { + const uint16_t *instructions; + uint8_t length; + int8_t origin; // required instruction memory origin or -1 + uint8_t pio_version; +} pio_program_t; + +typedef struct { + uint32_t content[4]; +} pio_sm_config; + +typedef struct pio_instance *PIO; +typedef const struct pio_chip PIO_CHIP_T; + +struct pio_chip { + const char *name; + const char *compatible; + uint16_t instr_count; + uint16_t sm_count; + uint16_t fifo_depth; + void *hw_state; + + PIO (*create_instance)(PIO_CHIP_T *chip, uint index); + int (*open_instance)(PIO pio); + void (*close_instance)(PIO pio); + + int (*pio_sm_config_xfer)(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count); + int (*pio_sm_xfer_data)(PIO pio, uint sm, uint dir, uint data_bytes, void *data); + + bool (*pio_can_add_program_at_offset)(PIO pio, const pio_program_t *program, uint offset); + uint (*pio_add_program_at_offset)(PIO pio, const pio_program_t *program, uint offset); + bool (*pio_remove_program)(PIO pio, const pio_program_t *program, uint loaded_offset); + bool (*pio_clear_instruction_memory)(PIO pio); + uint (*pio_encode_delay)(PIO pio, uint cycles); + uint (*pio_encode_sideset)(PIO pio, uint sideset_bit_count, uint value); + uint (*pio_encode_sideset_opt)(PIO pio, uint sideset_bit_count, uint value); + uint (*pio_encode_jmp)(PIO pio, uint addr); + uint (*pio_encode_jmp_not_x)(PIO pio, uint addr); + uint (*pio_encode_jmp_x_dec)(PIO pio, uint addr); + uint (*pio_encode_jmp_not_y)(PIO pio, uint addr); + uint (*pio_encode_jmp_y_dec)(PIO pio, uint addr); + uint (*pio_encode_jmp_x_ne_y)(PIO pio, uint addr); + uint (*pio_encode_jmp_pin)(PIO pio, uint addr); + uint (*pio_encode_jmp_not_osre)(PIO pio, uint addr); + uint (*pio_encode_wait_gpio)(PIO pio, bool polarity, uint gpio); + uint (*pio_encode_wait_pin)(PIO pio, bool polarity, uint pin); + uint (*pio_encode_wait_irq)(PIO pio, bool polarity, bool relative, uint irq); + uint (*pio_encode_in)(PIO pio, enum pio_src_dest src, uint count); + uint (*pio_encode_out)(PIO pio, enum pio_src_dest dest, uint count); + uint (*pio_encode_push)(PIO pio, bool if_full, bool block); + uint (*pio_encode_pull)(PIO pio, bool if_empty, bool block); + uint (*pio_encode_mov)(PIO pio, enum pio_src_dest dest, enum pio_src_dest src); + uint (*pio_encode_mov_not)(PIO pio, enum pio_src_dest dest, enum pio_src_dest src); + uint (*pio_encode_mov_reverse)(PIO pio, enum pio_src_dest dest, enum pio_src_dest src); + uint (*pio_encode_irq_set)(PIO pio, bool relative, uint irq); + uint (*pio_encode_irq_wait)(PIO pio, bool relative, uint irq); + uint (*pio_encode_irq_clear)(PIO pio, bool relative, uint irq); + uint (*pio_encode_set)(PIO pio, enum pio_src_dest dest, uint value); + uint (*pio_encode_nop)(PIO pio); + + bool (*pio_sm_claim)(PIO pio, uint sm); + bool (*pio_sm_claim_mask)(PIO pio, uint mask); + int (*pio_sm_claim_unused)(PIO pio, bool required); + bool (*pio_sm_unclaim)(PIO pio, uint sm); + bool (*pio_sm_is_claimed)(PIO pio, uint sm); + + void (*pio_sm_init)(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config); + void (*pio_sm_set_config)(PIO pio, uint sm, const pio_sm_config *config); + void (*pio_sm_exec)(PIO pio, uint sm, uint instr, bool blocking); + void (*pio_sm_clear_fifos)(PIO pio, uint sm); + void (*pio_sm_set_clkdiv_int_frac)(PIO pio, uint sm, uint16_t div_int, uint8_t div_frac); + void (*pio_sm_set_clkdiv)(PIO pio, uint sm, float div); + void (*pio_sm_set_pins)(PIO pio, uint sm, uint32_t pin_values); + void (*pio_sm_set_pins_with_mask)(PIO pio, uint sm, uint32_t pin_values, uint32_t pin_mask); + void (*pio_sm_set_pindirs_with_mask)(PIO pio, uint sm, uint32_t pin_dirs, uint32_t pin_mask); + void (*pio_sm_set_consecutive_pindirs)(PIO pio, uint sm, uint pin_base, uint pin_count, bool is_out); + void (*pio_sm_set_enabled)(PIO pio, uint sm, bool enabled); + void (*pio_sm_set_enabled_mask)(PIO pio, uint32_t mask, bool enabled); + void (*pio_sm_restart)(PIO pio, uint sm); + void (*pio_sm_restart_mask)(PIO pio, uint32_t mask); + void (*pio_sm_clkdiv_restart)(PIO pio, uint sm); + void (*pio_sm_clkdiv_restart_mask)(PIO pio, uint32_t mask); + void (*pio_sm_enable_sync)(PIO pio, uint32_t mask); + void (*pio_sm_put)(PIO pio, uint sm, uint32_t data, bool blocking); + uint32_t (*pio_sm_get)(PIO pio, uint sm, bool blocking); + void (*pio_sm_set_dmactrl)(PIO pio, uint sm, bool is_tx, uint32_t ctrl); + bool (*pio_sm_is_rx_fifo_empty)(PIO pio, uint sm); + bool (*pio_sm_is_rx_fifo_full)(PIO pio, uint sm); + uint (*pio_sm_get_rx_fifo_level)(PIO pio, uint sm); + bool (*pio_sm_is_tx_fifo_empty)(PIO pio, uint sm); + bool (*pio_sm_is_tx_fifo_full)(PIO pio, uint sm); + uint (*pio_sm_get_tx_fifo_level)(PIO pio, uint sm); + void (*pio_sm_drain_tx_fifo)(PIO pio, uint sm); + + pio_sm_config (*pio_get_default_sm_config)(PIO pio); + void (*smc_set_out_pins)(PIO pio, pio_sm_config *c, uint out_base, uint out_count); + void (*smc_set_set_pins)(PIO pio, pio_sm_config *c, uint set_base, uint set_count); + void (*smc_set_in_pins)(PIO pio, pio_sm_config *c, uint in_base); + void (*smc_set_sideset_pins)(PIO pio, pio_sm_config *c, uint sideset_base); + void (*smc_set_sideset)(PIO pio, pio_sm_config *c, uint bit_count, bool optional, bool pindirs); + void (*smc_set_clkdiv_int_frac)(PIO pio, pio_sm_config *c, uint16_t div_int, uint8_t div_frac); + void (*smc_set_clkdiv)(PIO pio, pio_sm_config *c, float div); + void (*smc_set_wrap)(PIO pio, pio_sm_config *c, uint wrap_target, uint wrap); + void (*smc_set_jmp_pin)(PIO pio, pio_sm_config *c, uint pin); + void (*smc_set_in_shift)(PIO pio, pio_sm_config *c, bool shift_right, bool autopush, uint push_threshold); + void (*smc_set_out_shift)(PIO pio, pio_sm_config *c, bool shift_right, bool autopull, uint pull_threshold); + void (*smc_set_fifo_join)(PIO pio, pio_sm_config *c, enum pio_fifo_join join); + void (*smc_set_out_special)(PIO pio, pio_sm_config *c, bool sticky, bool has_enable_pin, uint enable_pin_index); + void (*smc_set_mov_status)(PIO pio, pio_sm_config *c, enum pio_mov_status_type status_sel, uint status_n); + + uint32_t (*clock_get_hz)(PIO pio, enum clock_index clk_index); + void (*pio_gpio_init)(PIO, uint pin); + void (*gpio_init)(PIO pio, uint gpio); + void (*gpio_set_function)(PIO pio, uint gpio, enum gpio_function fn); + void (*gpio_set_pulls)(PIO pio, uint gpio, bool up, bool down); + void (*gpio_set_outover)(PIO pio, uint gpio, uint value); + void (*gpio_set_inover)(PIO pio, uint gpio, uint value); + void (*gpio_set_oeover)(PIO pio, uint gpio, uint value); + void (*gpio_set_input_enabled)(PIO pio, uint gpio, bool enabled); + void (*gpio_set_drive_strength)(PIO pio, uint gpio, enum gpio_drive_strength drive); +}; + +struct pio_instance { + const PIO_CHIP_T *chip; + int in_use; + bool errors_are_fatal; + bool error; +}; + +int pio_init(void); +PIO pio_open(uint idx); +PIO pio_open_by_name(const char *name); +PIO pio_open_helper(uint idx); +void pio_close(PIO pio); +void pio_panic(const char *msg); +int pio_get_index(PIO pio); +void pio_select(PIO pio); +PIO pio_get_current(void); + +static inline void pio_error(PIO pio, const char *msg) +{ + pio->error = true; + if (pio->errors_are_fatal) + pio_panic(msg); +} + +static inline bool pio_get_error(PIO pio) +{ + return pio->error; +} + +static inline void pio_clear_error(PIO pio) +{ + pio->error = false; +} + +static inline void pio_enable_fatal_errors(PIO pio, bool enable) +{ + pio->errors_are_fatal = enable; +} + +static inline uint pio_get_sm_count(PIO pio) +{ + return pio->chip->sm_count; +} + +static inline uint pio_get_instruction_count(PIO pio) +{ + return pio->chip->instr_count; +} + +static inline uint pio_get_fifo_depth(PIO pio) +{ + return pio->chip->fifo_depth; +} + +static inline void check_pio_param(__unused PIO pio) +{ + valid_params_if(PIO, pio_get_index(pio) >= 0); +} + +static inline int pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count) +{ + check_pio_param(pio); + return pio->chip->pio_sm_config_xfer(pio, sm, dir, buf_size, buf_count); +} + +static inline int pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data) +{ + check_pio_param(pio); + return pio->chip->pio_sm_xfer_data(pio, sm, dir, data_bytes, data); +} + +static inline bool pio_can_add_program(PIO pio, const pio_program_t *program) +{ + check_pio_param(pio); + return pio->chip->pio_can_add_program_at_offset(pio, program, PIO_ORIGIN_ANY); +} + +static inline bool pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) +{ + check_pio_param(pio); + return pio->chip->pio_can_add_program_at_offset(pio, program, offset); +} + +static inline uint pio_add_program(PIO pio, const pio_program_t *program) +{ + uint offset; + check_pio_param(pio); + offset = pio->chip->pio_add_program_at_offset(pio, program, PIO_ORIGIN_ANY); + if (offset == PIO_ORIGIN_INVALID) + pio_error(pio, "No program space"); + return offset; +} + +static inline void pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) +{ + check_pio_param(pio); + if (pio->chip->pio_add_program_at_offset(pio, program, offset) == PIO_ORIGIN_INVALID) + pio_error(pio, "No program space"); +} + +static inline void pio_remove_program(PIO pio, const pio_program_t *program, uint loaded_offset) +{ + check_pio_param(pio); + if (!pio->chip->pio_remove_program(pio, program, loaded_offset)) + pio_error(pio, "Failed to remove program"); +} + +static inline void pio_clear_instruction_memory(PIO pio) +{ + check_pio_param(pio); + if (!pio->chip->pio_clear_instruction_memory(pio)) + pio_error(pio, "Failed to clear instruction memory"); +} + +static inline uint pio_encode_delay(uint cycles) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_delay(pio, cycles); +} + +static inline uint pio_encode_sideset(uint sideset_bit_count, uint value) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_sideset(pio, sideset_bit_count, value); +} + +static inline uint pio_encode_sideset_opt(uint sideset_bit_count, uint value) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_sideset_opt(pio, sideset_bit_count, value); +} + +static inline uint pio_encode_jmp(uint addr) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_jmp(pio, addr); +} + +static inline uint pio_encode_jmp_not_x(uint addr) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_jmp_not_x(pio, addr); +} + +static inline uint pio_encode_jmp_x_dec(uint addr) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_jmp_x_dec(pio, addr); +} + +static inline uint pio_encode_jmp_not_y(uint addr) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_jmp_not_y(pio, addr); +} + +static inline uint pio_encode_jmp_y_dec(uint addr) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_jmp_y_dec(pio, addr); +} + +static inline uint pio_encode_jmp_x_ne_y(uint addr) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_jmp_x_ne_y(pio, addr); +} + +static inline uint pio_encode_jmp_pin(uint addr) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_jmp_pin(pio, addr); +} + +static inline uint pio_encode_jmp_not_osre(uint addr) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_jmp_not_osre(pio, addr); +} + +static inline uint pio_encode_wait_gpio(bool polarity, uint gpio) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_wait_gpio(pio, polarity, gpio); +} + +static inline uint pio_encode_wait_pin(bool polarity, uint pin) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_wait_pin(pio, polarity, pin); +} + +static inline uint pio_encode_wait_irq(bool polarity, bool relative, uint irq) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_wait_irq(pio, polarity, relative, irq); +} + +static inline uint pio_encode_in(enum pio_src_dest src, uint count) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_in(pio, src, count); +} + +static inline uint pio_encode_out(enum pio_src_dest dest, uint count) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_out(pio, dest, count); +} + +static inline uint pio_encode_push(bool if_full, bool block) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_push(pio, if_full, block); +} + +static inline uint pio_encode_pull(bool if_empty, bool block) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_pull(pio, if_empty, block); +} + +static inline uint pio_encode_mov(enum pio_src_dest dest, enum pio_src_dest src) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_mov(pio, dest, src); +} + +static inline uint pio_encode_mov_not(enum pio_src_dest dest, enum pio_src_dest src) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_mov_not(pio, dest, src); +} + +static inline uint pio_encode_mov_reverse(enum pio_src_dest dest, enum pio_src_dest src) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_mov_reverse(pio, dest, src); +} + +static inline uint pio_encode_irq_set(bool relative, uint irq) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_irq_set(pio, relative, irq); +} + +static inline uint pio_encode_irq_wait(bool relative, uint irq) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_irq_wait(pio, relative, irq); +} + +static inline uint pio_encode_irq_clear(bool relative, uint irq) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_irq_clear(pio, relative, irq); +} + +static inline uint pio_encode_set(enum pio_src_dest dest, uint value) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_set(pio, dest, value); +} + +static inline uint pio_encode_nop(void) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_encode_nop(pio); +} + +static inline void pio_sm_claim(PIO pio, uint sm) +{ + check_pio_param(pio); + if (!pio->chip->pio_sm_claim(pio, sm)) + pio_error(pio, "Failed to claim SM"); +} + +static inline void pio_claim_sm_mask(PIO pio, uint mask) +{ + check_pio_param(pio); + if (!pio->chip->pio_sm_claim_mask(pio, mask)) + pio_error(pio, "Failed to claim masked SMs"); +} + +static inline void pio_sm_unclaim(PIO pio, uint sm) +{ + check_pio_param(pio); + pio->chip->pio_sm_unclaim(pio, sm); +} + +static inline int pio_claim_unused_sm(PIO pio, bool required) +{ + check_pio_param(pio); + return pio->chip->pio_sm_claim_unused(pio, required); +} + +static inline bool pio_sm_is_claimed(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_is_claimed(pio, sm); +} + +static inline void pio_sm_init(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config) +{ + check_pio_param(pio); + pio->chip->pio_sm_init(pio, sm, initial_pc, config); +} + +static inline void pio_sm_set_config(PIO pio, uint sm, const pio_sm_config *config) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_config(pio, sm, config); +} + +static inline void pio_sm_exec(PIO pio, uint sm, uint instr) +{ + check_pio_param(pio); + pio->chip->pio_sm_exec(pio, sm, instr, false); +} + +static inline void pio_sm_exec_wait_blocking(PIO pio, uint sm, uint instr) +{ + check_pio_param(pio); + pio->chip->pio_sm_exec(pio, sm, instr, true); +} + +static inline void pio_sm_clear_fifos(PIO pio, uint sm) +{ + check_pio_param(pio); + pio->chip->pio_sm_clear_fifos(pio, sm); +} + +static inline void pio_sm_set_clkdiv_int_frac(PIO pio, uint sm, uint16_t div_int, uint8_t div_frac) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_clkdiv_int_frac(pio, sm, div_int, div_frac); +} + +static inline void pio_sm_set_clkdiv(PIO pio, uint sm, float div) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_clkdiv(pio, sm, div); +} + +static inline void pio_sm_set_pins(PIO pio, uint sm, uint32_t pin_values) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_pins(pio, sm, pin_values); +} + +static inline void pio_sm_set_pins_with_mask(PIO pio, uint sm, uint32_t pin_values, uint32_t pin_mask) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_pins_with_mask(pio, sm, pin_values, pin_mask); +} + +static inline void pio_sm_set_pindirs_with_mask(PIO pio, uint sm, uint32_t pin_dirs, uint32_t pin_mask) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_pindirs_with_mask(pio, sm, pin_dirs, pin_mask); +} + +static inline void pio_sm_set_consecutive_pindirs(PIO pio, uint sm, uint pin_base, uint pin_count, bool is_out) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, is_out); +} + +static inline void pio_sm_set_enabled(PIO pio, uint sm, bool enabled) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_enabled(pio, sm, enabled); +} + +static inline void pio_set_sm_mask_enabled(PIO pio, uint32_t mask, bool enabled) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_enabled_mask(pio, mask, enabled); +} + +static inline void pio_sm_restart(PIO pio, uint sm) +{ + check_pio_param(pio); + pio->chip->pio_sm_restart(pio, sm); +} + +static inline void pio_restart_sm_mask(PIO pio, uint32_t mask) +{ + check_pio_param(pio); + pio->chip->pio_sm_restart_mask(pio, mask); +} + +static inline void pio_sm_clkdiv_restart(PIO pio, uint sm) +{ + check_pio_param(pio); + pio->chip->pio_sm_clkdiv_restart(pio, sm); +} + +static inline void pio_clkdiv_restart_sm_mask(PIO pio, uint32_t mask) +{ + check_pio_param(pio); + pio->chip->pio_sm_clkdiv_restart_mask(pio, mask); +} + +static inline void pio_enable_sm_in_sync_mask(PIO pio, uint32_t mask) +{ + check_pio_param(pio); + pio->chip->pio_sm_enable_sync(pio, mask); +}; + +static inline void pio_sm_set_dmactrl(PIO pio, uint sm, bool is_tx, uint32_t ctrl) +{ + check_pio_param(pio); + pio->chip->pio_sm_set_dmactrl(pio, sm, is_tx, ctrl); +}; + +static inline bool pio_sm_is_rx_fifo_empty(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_is_rx_fifo_empty(pio, sm); +} + +static inline bool pio_sm_is_rx_fifo_full(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_is_rx_fifo_full(pio, sm); +} + +static inline uint pio_sm_get_rx_fifo_level(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_get_rx_fifo_level(pio, sm); +} + +static inline bool pio_sm_is_tx_fifo_empty(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_is_tx_fifo_empty(pio, sm); +} + +static inline bool pio_sm_is_tx_fifo_full(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_is_tx_fifo_full(pio, sm); +} + +static inline uint pio_sm_get_tx_fifo_level(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_get_tx_fifo_level(pio, sm); +} + +static inline void pio_sm_drain_tx_fifo(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_drain_tx_fifo(pio, sm); +} + +static inline void pio_sm_put(PIO pio, uint sm, uint32_t data) +{ + check_pio_param(pio); + pio->chip->pio_sm_put(pio, sm, data, false); +} + +static inline void pio_sm_put_blocking(PIO pio, uint sm, uint32_t data) +{ + check_pio_param(pio); + pio->chip->pio_sm_put(pio, sm, data, true); +} + +static inline uint32_t pio_sm_get(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_get(pio, sm, false); +} + +static inline uint32_t pio_sm_get_blocking(PIO pio, uint sm) +{ + check_pio_param(pio); + return pio->chip->pio_sm_get(pio, sm, true); +} + +static inline pio_sm_config pio_get_default_sm_config_for_pio(PIO pio) +{ + check_pio_param(pio); + return pio->chip->pio_get_default_sm_config(pio); +} + +static inline pio_sm_config pio_get_default_sm_config(void) +{ + PIO pio = pio_get_current(); + return pio->chip->pio_get_default_sm_config(pio); +} + +static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_out_pins(pio, c, out_base, out_count); +} + +static inline void sm_config_set_set_pins(pio_sm_config *c, uint set_base, uint set_count) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_set_pins(pio, c, set_base, set_count); +} + +static inline void sm_config_set_in_pins(pio_sm_config *c, uint in_base) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_in_pins(pio, c, in_base); +} + +static inline void sm_config_set_sideset_pins(pio_sm_config *c, uint sideset_base) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_sideset_pins(pio, c, sideset_base); +} + +static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count, bool optional, bool pindirs) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_sideset(pio, c, bit_count, optional, pindirs); +} + +static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c, uint16_t div_int, uint8_t div_frac) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_clkdiv_int_frac(pio, c, div_int, div_frac); +} + +static inline void sm_config_set_clkdiv(pio_sm_config *c, float div) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_clkdiv(pio, c, div); +} + +static inline void sm_config_set_wrap(pio_sm_config *c, uint wrap_target, uint wrap) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_wrap(pio, c, wrap_target, wrap); +} + +static inline void sm_config_set_jmp_pin(pio_sm_config *c, uint pin) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_jmp_pin(pio, c, pin); +} + +static inline void sm_config_set_in_shift(pio_sm_config *c, bool shift_right, bool autopush, uint push_threshold) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_in_shift(pio, c, shift_right, autopush, push_threshold); +} + +static inline void sm_config_set_out_shift(pio_sm_config *c, bool shift_right, bool autopull, uint pull_threshold) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_out_shift(pio, c, shift_right, autopull, pull_threshold); +} + +static inline void sm_config_set_fifo_join(pio_sm_config *c, enum pio_fifo_join join) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_fifo_join(pio, c, join); +} + +static inline void sm_config_set_out_special(pio_sm_config *c, bool sticky, bool has_enable_pin, uint enable_pin_index) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_out_special(pio, c, sticky, has_enable_pin, enable_pin_index); +} + +static inline void sm_config_set_mov_status(pio_sm_config *c, enum pio_mov_status_type status_sel, uint status_n) +{ + PIO pio = pio_get_current(); + pio->chip->smc_set_mov_status(pio, c, status_sel, status_n); +} + +static inline void pio_gpio_init(PIO pio, uint pin) +{ + check_pio_param(pio); + pio->chip->pio_gpio_init(pio, pin); +} + +static inline uint32_t clock_get_hz(enum clock_index clk_index) +{ + PIO pio = pio_get_current(); + return pio->chip->clock_get_hz(pio, clk_index); +} + +static inline void gpio_init(uint gpio) +{ + PIO pio = pio_get_current(); + pio->chip->gpio_init(pio, gpio); +} + +static inline void gpio_set_function(uint gpio, enum gpio_function fn) +{ + PIO pio = pio_get_current(); + pio->chip->gpio_set_function(pio, gpio, fn); +} + + +static inline void gpio_set_pulls(uint gpio, bool up, bool down) +{ + PIO pio = pio_get_current(); + pio->chip->gpio_set_pulls(pio, gpio, up, down); +} + +static inline void gpio_set_outover(uint gpio, uint value) +{ + PIO pio = pio_get_current(); + pio->chip->gpio_set_outover(pio, gpio, value); +} + +static inline void gpio_set_inover(uint gpio, uint value) +{ + PIO pio = pio_get_current(); + pio->chip->gpio_set_inover(pio, gpio, value); +} + +static inline void gpio_set_oeover(uint gpio, uint value) +{ + PIO pio = pio_get_current(); + pio->chip->gpio_set_oeover(pio, gpio, value); +} + +static inline void gpio_set_input_enabled(uint gpio, bool enabled) +{ + PIO pio = pio_get_current(); + pio->chip->gpio_set_input_enabled(pio, gpio, enabled); +} + +static inline void gpio_set_drive_strength(uint gpio, enum gpio_drive_strength drive) +{ + PIO pio = pio_get_current(); + pio->chip->gpio_set_drive_strength(pio, gpio, drive); +} + +static inline void gpio_pull_up(uint gpio) { + gpio_set_pulls(gpio, true, false); +} + +static inline void gpio_pull_down(uint gpio) { + gpio_set_pulls(gpio, false, true); +} + +static inline void gpio_disable_pulls(uint gpio) { + gpio_set_pulls(gpio, false, false); +} + +static inline void stdio_init_all(void) +{ +} + +void sleep_us(uint64_t us); + +static inline void sleep_ms(uint32_t ms) { + sleep_us((uint64_t)(ms * (uint64_t)1000)); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/piolib_priv.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/piolib_priv.h new file mode 100644 index 000000000000..6dcea55428ae --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/piolib_priv.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023-24 Raspberry Pi Ltd. + * All rights reserved. + */ + +#ifndef _PIOLIB_PRIV_H +#define _PIOLIB_PRIV_H + +#include "pio_platform.h" + +#define DECLARE_PIO_CHIP(chip) \ + const PIO_CHIP_T *__ptr_ ## chip __attribute__ ((section ("piochips"))) __attribute__ ((used)) = \ + &chip + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/rp1_pio_if.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/rp1_pio_if.h new file mode 100644 index 000000000000..a8a59bd21146 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/include/rp1_pio_if.h @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023-24 Raspberry Pi Ltd. + * All rights reserved. + */ +#ifndef _RP1_PIO_IF_H +#define _RP1_PIO_IF_H + +#include + +#define RP1_PIO_INSTRUCTION_COUNT 32 +#define RP1_PIO_SM_COUNT 4 +#define RP1_PIO_GPIO_COUNT 28 +#define RP1_GPIO_FUNC_PIO 7 + +#define RP1_PIO_ORIGIN_ANY ((uint16_t)(~0)) + +#define RP1_PIO_DIR_TO_SM 0 +#define RP1_PIO_DIR_FROM_SM 1 +#define RP1_PIO_DIR_COUNT 2 + +typedef struct { + uint32_t clkdiv; + uint32_t execctrl; + uint32_t shiftctrl; + uint32_t pinctrl; +} rp1_pio_sm_config; + +struct rp1_pio_add_program_args { + uint16_t num_instrs; + uint16_t origin; + uint16_t instrs[RP1_PIO_INSTRUCTION_COUNT]; +}; + +struct rp1_pio_remove_program_args { + uint16_t num_instrs; + uint16_t origin; +}; + +struct rp1_pio_sm_claim_args { + uint16_t mask; +}; + +struct rp1_pio_sm_init_args { + uint16_t sm; + uint16_t initial_pc; + rp1_pio_sm_config config; +}; + +struct rp1_pio_sm_set_config_args { + uint16_t sm; + uint16_t rsvd; + rp1_pio_sm_config config; +}; + +struct rp1_pio_sm_exec_args { + uint16_t sm; + uint16_t instr; + uint8_t blocking; + uint8_t rsvd; +}; + +struct rp1_pio_sm_clear_fifos_args { + uint16_t sm; +}; + +struct rp1_pio_sm_set_clkdiv_args { + uint16_t sm; + uint16_t div_int; + uint8_t div_frac; + uint8_t rsvd; +}; + +struct rp1_pio_sm_set_pins_args { + uint16_t sm; + uint16_t rsvd; + uint32_t values; + uint32_t mask; +}; + +struct rp1_pio_sm_set_pindirs_args { + uint16_t sm; + uint16_t rsvd; + uint32_t dirs; + uint32_t mask; +}; + +struct rp1_pio_sm_set_enabled_args { + uint16_t mask; + uint8_t enable; + uint8_t rsvd; +}; + +struct rp1_pio_sm_restart_args { + uint16_t mask; +}; + +struct rp1_pio_sm_clkdiv_restart_args { + uint16_t mask; +}; + +struct rp1_pio_sm_enable_sync_args { + uint16_t mask; +}; + +struct rp1_pio_sm_put_args { + uint16_t sm; + uint8_t blocking; + uint8_t rsvd; + uint32_t data; +}; + +struct rp1_pio_sm_get_args { + uint16_t sm; + uint8_t blocking; + uint8_t rsvd; + uint32_t data; /* OUT */ +}; + +struct rp1_pio_sm_set_dmactrl_args { + uint16_t sm; + uint8_t is_tx; + uint8_t rsvd; + uint32_t ctrl; +}; + +struct rp1_pio_sm_fifo_state_args { + uint16_t sm; + uint8_t tx; + uint8_t rsvd; + uint16_t level; /* OUT */ + uint8_t empty; /* OUT */ + uint8_t full; /* OUT */ +}; + +struct rp1_gpio_init_args { + uint16_t gpio; +}; + +struct rp1_gpio_set_function_args { + uint16_t gpio; + uint16_t fn; +}; + +struct rp1_gpio_set_pulls_args { + uint16_t gpio; + uint8_t up; + uint8_t down; +}; + +struct rp1_gpio_set_args { + uint16_t gpio; + uint16_t value; +}; + +struct rp1_pio_sm_config_xfer_args { + uint16_t sm; + uint16_t dir; + uint16_t buf_size; + uint16_t buf_count; +}; + +struct rp1_pio_sm_config_xfer32_args { + uint16_t sm; + uint16_t dir; + uint32_t buf_size; + uint32_t buf_count; +}; + +struct rp1_pio_sm_xfer_data_args { + uint16_t sm; + uint16_t dir; + uint16_t data_bytes; + uint16_t rsvd; + void *data; +}; + +struct rp1_pio_sm_xfer_data32_args { + uint16_t sm; + uint16_t dir; + uint32_t data_bytes; + void *data; +}; + +struct rp1_access_hw_args { + uint32_t addr; + uint32_t len; + void *data; +}; + +#define PIO_IOC_MAGIC 102 + +#define PIO_IOC_SM_CONFIG_XFER _IOW(PIO_IOC_MAGIC, 0, struct rp1_pio_sm_config_xfer_args) +#define PIO_IOC_SM_XFER_DATA _IOW(PIO_IOC_MAGIC, 1, struct rp1_pio_sm_xfer_data_args) +#define PIO_IOC_SM_XFER_DATA32 _IOW(PIO_IOC_MAGIC, 2, struct rp1_pio_sm_xfer_data32_args) +#define PIO_IOC_SM_CONFIG_XFER32 _IOW(PIO_IOC_MAGIC, 3, struct rp1_pio_sm_config_xfer32_args) + +#define PIO_IOC_READ_HW _IOW(PIO_IOC_MAGIC, 8, struct rp1_access_hw_args) +#define PIO_IOC_WRITE_HW _IOW(PIO_IOC_MAGIC, 9, struct rp1_access_hw_args) + +#define PIO_IOC_CAN_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 10, struct rp1_pio_add_program_args) +#define PIO_IOC_ADD_PROGRAM _IOW(PIO_IOC_MAGIC, 11, struct rp1_pio_add_program_args) +#define PIO_IOC_REMOVE_PROGRAM _IOW(PIO_IOC_MAGIC, 12, struct rp1_pio_remove_program_args) +#define PIO_IOC_CLEAR_INSTR_MEM _IO(PIO_IOC_MAGIC, 13) + +#define PIO_IOC_SM_CLAIM _IOW(PIO_IOC_MAGIC, 20, struct rp1_pio_sm_claim_args) +#define PIO_IOC_SM_UNCLAIM _IOW(PIO_IOC_MAGIC, 21, struct rp1_pio_sm_claim_args) +#define PIO_IOC_SM_IS_CLAIMED _IOW(PIO_IOC_MAGIC, 22, struct rp1_pio_sm_claim_args) + +#define PIO_IOC_SM_INIT _IOW(PIO_IOC_MAGIC, 30, struct rp1_pio_sm_init_args) +#define PIO_IOC_SM_SET_CONFIG _IOW(PIO_IOC_MAGIC, 31, struct rp1_pio_sm_set_config_args) +#define PIO_IOC_SM_EXEC _IOW(PIO_IOC_MAGIC, 32, struct rp1_pio_sm_exec_args) +#define PIO_IOC_SM_CLEAR_FIFOS _IOW(PIO_IOC_MAGIC, 33, struct rp1_pio_sm_clear_fifos_args) +#define PIO_IOC_SM_SET_CLKDIV _IOW(PIO_IOC_MAGIC, 34, struct rp1_pio_sm_set_clkdiv_args) +#define PIO_IOC_SM_SET_PINS _IOW(PIO_IOC_MAGIC, 35, struct rp1_pio_sm_set_pins_args) +#define PIO_IOC_SM_SET_PINDIRS _IOW(PIO_IOC_MAGIC, 36, struct rp1_pio_sm_set_pindirs_args) +#define PIO_IOC_SM_SET_ENABLED _IOW(PIO_IOC_MAGIC, 37, struct rp1_pio_sm_set_enabled_args) +#define PIO_IOC_SM_RESTART _IOW(PIO_IOC_MAGIC, 38, struct rp1_pio_sm_restart_args) +#define PIO_IOC_SM_CLKDIV_RESTART _IOW(PIO_IOC_MAGIC, 39, struct rp1_pio_sm_restart_args) +#define PIO_IOC_SM_ENABLE_SYNC _IOW(PIO_IOC_MAGIC, 40, struct rp1_pio_sm_enable_sync_args) +#define PIO_IOC_SM_PUT _IOW(PIO_IOC_MAGIC, 41, struct rp1_pio_sm_put_args) +#define PIO_IOC_SM_GET _IOWR(PIO_IOC_MAGIC, 42, struct rp1_pio_sm_get_args) +#define PIO_IOC_SM_SET_DMACTRL _IOW(PIO_IOC_MAGIC, 43, struct rp1_pio_sm_set_dmactrl_args) +#define PIO_IOC_SM_FIFO_STATE _IOW(PIO_IOC_MAGIC, 44, struct rp1_pio_sm_fifo_state_args) +#define PIO_IOC_SM_DRAIN_TX _IOW(PIO_IOC_MAGIC, 45, struct rp1_pio_sm_clear_fifos_args) + +#define PIO_IOC_GPIO_INIT _IOW(PIO_IOC_MAGIC, 50, struct rp1_gpio_init_args) +#define PIO_IOC_GPIO_SET_FUNCTION _IOW(PIO_IOC_MAGIC, 51, struct rp1_gpio_set_function_args) +#define PIO_IOC_GPIO_SET_PULLS _IOW(PIO_IOC_MAGIC, 52, struct rp1_gpio_set_pulls_args) +#define PIO_IOC_GPIO_SET_OUTOVER _IOW(PIO_IOC_MAGIC, 53, struct rp1_gpio_set_args) +#define PIO_IOC_GPIO_SET_INOVER _IOW(PIO_IOC_MAGIC, 54, struct rp1_gpio_set_args) +#define PIO_IOC_GPIO_SET_OEOVER _IOW(PIO_IOC_MAGIC, 55, struct rp1_gpio_set_args) +#define PIO_IOC_GPIO_SET_INPUT_ENABLED _IOW(PIO_IOC_MAGIC, 56, struct rp1_gpio_set_args) +#define PIO_IOC_GPIO_SET_DRIVE_STRENGTH _IOW(PIO_IOC_MAGIC, 57, struct rp1_gpio_set_args) + +#endif diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/pio_rp1.c b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/pio_rp1.c new file mode 100644 index 000000000000..b961eea9a15f --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/pio_rp1.c @@ -0,0 +1,974 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 Raspberry Pi Ltd. + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define PIOLIB_INTERNALS + +#include "pio_platform.h" + +#define pio_encode_delay _pio_encode_delay +#define pio_encode_sideset _pio_encode_sideset +#define pio_encode_sideset_opt _pio_encode_sideset_opt +#define pio_encode_jmp _pio_encode_jmp +#define pio_encode_jmp_not_x _pio_encode_jmp_not_x +#define pio_encode_jmp_x_dec _pio_encode_jmp_x_dec +#define pio_encode_jmp_not_y _pio_encode_jmp_not_y +#define pio_encode_jmp_y_dec _pio_encode_jmp_y_dec +#define pio_encode_jmp_x_ne_y _pio_encode_jmp_x_ne_y +#define pio_encode_jmp_pin _pio_encode_jmp_pin +#define pio_encode_jmp_not_osre _pio_encode_jmp_not_osre +#define pio_encode_wait_gpio _pio_encode_wait_gpio +#define pio_encode_wait_pin _pio_encode_wait_pin +#define pio_encode_wait_irq _pio_encode_wait_irq +#define pio_encode_in _pio_encode_in +#define pio_encode_out _pio_encode_out +#define pio_encode_push _pio_encode_push +#define pio_encode_pull _pio_encode_pull +#define pio_encode_mov _pio_encode_mov +#define pio_encode_mov_not _pio_encode_mov_not +#define pio_encode_mov_reverse _pio_encode_mov_reverse +#define pio_encode_irq_set _pio_encode_irq_set +#define pio_encode_irq_wait _pio_encode_irq_wait +#define pio_encode_irq_clear _pio_encode_irq_clear +#define pio_encode_set _pio_encode_set +#define pio_encode_nop _pio_encode_nop +#include "hardware/pio_instructions.h" +#undef pio_encode_delay +#undef pio_encode_sideset +#undef pio_encode_sideset_opt +#undef pio_encode_jmp +#undef pio_encode_jmp_not_x +#undef pio_encode_jmp_x_dec +#undef pio_encode_jmp_not_y +#undef pio_encode_jmp_y_dec +#undef pio_encode_jmp_x_ne_y +#undef pio_encode_jmp_pin +#undef pio_encode_jmp_not_osre +#undef pio_encode_wait_gpio +#undef pio_encode_wait_pin +#undef pio_encode_wait_irq +#undef pio_encode_in +#undef pio_encode_out +#undef pio_encode_push +#undef pio_encode_pull +#undef pio_encode_mov +#undef pio_encode_mov_not +#undef pio_encode_mov_reverse +#undef pio_encode_irq_set +#undef pio_encode_irq_wait +#undef pio_encode_irq_clear +#undef pio_encode_set +#undef pio_encode_nop + +#include "piolib.h" +#include "piolib_priv.h" +#include "hardware/gpio.h" +#include "hardware/pio.h" +#include "hardware/regs/proc_pio.h" +#include "rp1_pio_if.h" + +typedef struct rp1_pio_handle { + struct pio_instance base; + const char *devname; + int fd; +} *RP1_PIO; + +#define smc_to_rp1(_config, _c) rp1_pio_sm_config *_c = (rp1_pio_sm_config*)_config + +#define GPIOS_MASK ((1 << RP1_PIO_GPIO_COUNT) - 1) + +STATIC_ASSERT(sizeof(rp1_pio_sm_config) <= sizeof(pio_sm_config)); + +static inline void check_sm_param(__unused uint sm) +{ + valid_params_if(PIO, sm < RP1_PIO_SM_COUNT); +} + +static inline void check_sm_mask(__unused uint mask) +{ + valid_params_if(PIO, mask < (1u << RP1_PIO_SM_COUNT)); +} + +static pio_sm_config rp1_pio_get_default_sm_config(PIO) +{ + pio_sm_config c = { { 0 } }; + sm_config_set_clkdiv_int_frac(&c, 1, 0); + sm_config_set_wrap(&c, 0, 31); + sm_config_set_in_shift(&c, true, false, 32); + sm_config_set_out_shift(&c, true, false, 32); + return c; +} + +static uint rp1_pio_encode_delay(PIO, uint cycles) +{ + return _pio_encode_delay(cycles); +} + +static uint rp1_pio_encode_sideset(PIO, uint sideset_bit_count, uint value) +{ + return _pio_encode_sideset(sideset_bit_count, value); +} + +static uint rp1_pio_encode_sideset_opt(PIO, uint sideset_bit_count, uint value) +{ + return _pio_encode_sideset_opt(sideset_bit_count, value); +} + +static uint rp1_pio_encode_jmp(PIO, uint addr) +{ + return _pio_encode_jmp(addr); +} + +static uint rp1_pio_encode_jmp_not_x(PIO, uint addr) +{ + return _pio_encode_jmp_not_x(addr); +} + +static uint rp1_pio_encode_jmp_x_dec(PIO, uint addr) +{ + return _pio_encode_jmp_x_dec(addr); +} + +static uint rp1_pio_encode_jmp_not_y(PIO, uint addr) +{ + return _pio_encode_jmp_not_y(addr); +} + +static uint rp1_pio_encode_jmp_y_dec(PIO, uint addr) +{ + return _pio_encode_jmp_y_dec(addr); +} + +static uint rp1_pio_encode_jmp_x_ne_y(PIO, uint addr) +{ + return _pio_encode_jmp_x_ne_y(addr); +} + +static uint rp1_pio_encode_jmp_pin(PIO, uint addr) +{ + return _pio_encode_jmp_pin(addr); +} + +static uint rp1_pio_encode_jmp_not_osre(PIO, uint addr) +{ + return _pio_encode_jmp_not_osre(addr); +} + +static uint rp1_pio_encode_wait_gpio(PIO, bool polarity, uint gpio) +{ + return _pio_encode_wait_gpio(polarity, gpio); +} + +static uint rp1_pio_encode_wait_pin(PIO, bool polarity, uint pin) +{ + return _pio_encode_wait_pin(polarity, pin); +} + +static uint rp1_pio_encode_wait_irq(PIO, bool polarity, bool relative, uint irq) +{ + return _pio_encode_wait_irq(polarity, relative, irq); +} + +static uint rp1_pio_encode_in(PIO, enum pio_src_dest src, uint count) +{ + return _pio_encode_in(src, count); +} + +static uint rp1_pio_encode_out(PIO, enum pio_src_dest dest, uint count) +{ + return _pio_encode_out(dest, count); +} + +static uint rp1_pio_encode_push(PIO, bool if_full, bool block) +{ + return _pio_encode_push(if_full, block); +} + +static uint rp1_pio_encode_pull(PIO, bool if_empty, bool block) +{ + return _pio_encode_pull(if_empty, block); +} + +static uint rp1_pio_encode_mov(PIO, enum pio_src_dest dest, enum pio_src_dest src) +{ + return _pio_encode_mov(dest, src); +} + +static uint rp1_pio_encode_mov_not(PIO, enum pio_src_dest dest, enum pio_src_dest src) +{ + return _pio_encode_mov_not(dest, src); +} + +static uint rp1_pio_encode_mov_reverse(PIO, enum pio_src_dest dest, enum pio_src_dest src) +{ + return _pio_encode_mov_reverse(dest, src); +} + +static uint rp1_pio_encode_irq_set(PIO, bool relative, uint irq) +{ + return _pio_encode_irq_set(relative, irq); +} + +static uint rp1_pio_encode_irq_wait(PIO, bool relative, uint irq) +{ + return _pio_encode_irq_wait(relative, irq); +} + +static uint rp1_pio_encode_irq_clear(PIO, bool relative, uint irq) +{ + return _pio_encode_irq_clear(relative, irq); +} + +static uint rp1_pio_encode_set(PIO, enum pio_src_dest dest, uint value) +{ + return _pio_encode_set(dest, value); +} + +static uint rp1_pio_encode_nop(PIO) +{ + return _pio_encode_nop(); +} + +static int rp1_ioctl(PIO pio, int request, void *args) +{ + RP1_PIO rp = (RP1_PIO)pio; + int err = ioctl(rp->fd, request, args); + switch (err) { + case -EREMOTEIO: + case -ETIMEDOUT: + pio_panic("Error communicating with RP1"); + break; + default: + break; + } + return err; +} + +static int rp1_pio_sm_config_xfer(PIO pio, uint sm, uint dir, uint buf_size, uint buf_count) +{ + struct rp1_pio_sm_config_xfer_args args = { .sm = sm, .dir = dir, .buf_size = buf_size, .buf_count = buf_count }; + struct rp1_pio_sm_config_xfer32_args args32 = { .sm = sm, .dir = dir, .buf_size = buf_size, .buf_count = buf_count }; + int err; + check_sm_param(sm); + if (buf_size > 0xffff || buf_count > 0xffff) + err = rp1_ioctl(pio, PIO_IOC_SM_CONFIG_XFER32, &args32); + else + err = rp1_ioctl(pio, PIO_IOC_SM_CONFIG_XFER, &args); + return err; +} + +static int rp1_pio_sm_xfer_data(PIO pio, uint sm, uint dir, uint data_bytes, void *data) +{ + struct rp1_pio_sm_xfer_data_args args = { .sm = sm, .dir = dir, .data_bytes = data_bytes, .rsvd = 0, .data = data }; + struct rp1_pio_sm_xfer_data32_args args32 = { .sm = sm, .dir = dir, .data_bytes = data_bytes, .data = data }; + int err; + check_sm_param(sm); + if (data_bytes > 0xffff) + err = rp1_ioctl(pio, PIO_IOC_SM_XFER_DATA32, &args32); + else + err = rp1_ioctl(pio, PIO_IOC_SM_XFER_DATA, &args); + return err; +} + +static bool rp1_pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) +{ + struct rp1_pio_add_program_args args = { .num_instrs = program->length, .origin = program->origin }; + int err; + valid_params_if(PIO, offset < RP1_PIO_INSTRUCTION_COUNT || offset == PIO_ORIGIN_ANY); + valid_params_if(PIO, program->length <= RP1_PIO_INSTRUCTION_COUNT); + valid_params_if(PIO, offset + program->length <= RP1_PIO_INSTRUCTION_COUNT || offset == PIO_ORIGIN_ANY); + if (program->origin >= 0 && (uint)program->origin != offset) + return false; + if (offset != PIO_ORIGIN_ANY) + args.origin = offset; + memcpy(args.instrs, program->instructions, program->length * sizeof(uint16_t)); + err = rp1_ioctl(pio, PIO_IOC_CAN_ADD_PROGRAM, &args); + return (err > 0); +} + +static uint rp1_pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset) +{ + struct rp1_pio_add_program_args args = { .num_instrs = program->length, .origin = program->origin }; + valid_params_if(PIO, offset < RP1_PIO_INSTRUCTION_COUNT || offset == PIO_ORIGIN_ANY); + valid_params_if(PIO, program->length <= RP1_PIO_INSTRUCTION_COUNT); + valid_params_if(PIO, offset + program->length <= RP1_PIO_INSTRUCTION_COUNT || offset == PIO_ORIGIN_ANY); + if (offset != PIO_ORIGIN_ANY) + args.origin = offset; + memcpy(args.instrs, program->instructions, program->length * sizeof(uint16_t)); + return rp1_ioctl(pio, PIO_IOC_ADD_PROGRAM, &args); +} + +static bool rp1_pio_remove_program(PIO pio, const pio_program_t *program, uint offset) +{ + struct rp1_pio_remove_program_args args = { .num_instrs = program->length, .origin = offset }; + valid_params_if(PIO, offset < RP1_PIO_INSTRUCTION_COUNT); + valid_params_if(PIO, offset + program->length <= RP1_PIO_INSTRUCTION_COUNT); + return !rp1_ioctl(pio, PIO_IOC_REMOVE_PROGRAM, &args); +} + +static bool rp1_pio_clear_instruction_memory(PIO pio) +{ + return !rp1_ioctl(pio, PIO_IOC_CLEAR_INSTR_MEM, NULL); +} + +static bool rp1_pio_sm_claim(PIO pio, uint sm) +{ + struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; + check_sm_param(sm); + return (rp1_ioctl(pio, PIO_IOC_SM_CLAIM, &args) >= 0); +} + +static bool rp1_pio_sm_claim_mask(PIO pio, uint mask) +{ + struct rp1_pio_sm_claim_args args = { .mask = mask }; + valid_params_if(PIO, !!mask); + check_sm_mask(mask); + return (rp1_ioctl(pio, PIO_IOC_SM_CLAIM, &args) >= 0); +} + +static bool rp1_pio_sm_unclaim(PIO pio, uint sm) +{ + struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; + check_sm_param(sm); + return !rp1_ioctl(pio, PIO_IOC_SM_UNCLAIM, &args); +} + +static int rp1_pio_sm_claim_unused(PIO pio, bool required) +{ + struct rp1_pio_sm_claim_args args = { .mask = 0 }; + int sm = rp1_ioctl(pio, PIO_IOC_SM_CLAIM, &args); + if (sm < 0 && required) + pio_panic("No PIO state machines are available"); + return sm; +} + +static bool rp1_pio_sm_is_claimed(PIO pio, uint sm) +{ + struct rp1_pio_sm_claim_args args = { .mask = (1 << sm) }; + check_sm_param(sm); + int err = rp1_ioctl(pio, PIO_IOC_SM_IS_CLAIMED, &args); + return (err > 0); +} + +static void rp1_pio_sm_init(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config) +{ + smc_to_rp1(config, c); + struct rp1_pio_sm_init_args args = { .sm = sm, .initial_pc = initial_pc, .config = *c }; + valid_params_if(PIO, initial_pc < RP1_PIO_INSTRUCTION_COUNT); + + (void)rp1_ioctl(pio, PIO_IOC_SM_INIT, &args); +} + +static void rp1_pio_sm_set_config(PIO pio, uint sm, const pio_sm_config *config) +{ + smc_to_rp1(config, c); + struct rp1_pio_sm_init_args args = { .sm = sm, .config = *c }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_SET_CONFIG, &args); +} + +static void rp1_pio_sm_exec(PIO pio, uint sm, uint instr, bool blocking) +{ + struct rp1_pio_sm_exec_args args = { .sm = sm, .instr = instr, .blocking = blocking }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_EXEC, &args); +} + +static void rp1_pio_sm_clear_fifos(PIO pio, uint sm) +{ + struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_CLEAR_FIFOS, &args); +} + +static void rp1_pio_calculate_clkdiv_from_float(float div, uint16_t *div_int, uint8_t *div_frac) +{ + valid_params_if(PIO, div >= 1 && div <= 65536); + *div_int = (uint16_t)div; + if (*div_int == 0) { + *div_frac = 0; + } else { + *div_frac = (uint8_t)((div - (float)*div_int) * (1u << 8u)); + } +} + +static void rp1_pio_sm_set_clkdiv_int_frac(PIO pio, uint sm, uint16_t div_int, uint8_t div_frac) +{ + struct rp1_pio_sm_set_clkdiv_args args = { .sm = sm, .div_int = div_int, .div_frac = div_frac }; + + check_sm_param(sm); + invalid_params_if(PIO, div_int == 0 && div_frac != 0); + (void)rp1_ioctl(pio, PIO_IOC_SM_SET_CLKDIV, &args); +} + +static void rp1_pio_sm_set_clkdiv(PIO pio, uint sm, float div) +{ + uint16_t div_int; + uint8_t div_frac; + + check_sm_param(sm); + rp1_pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); + rp1_pio_sm_set_clkdiv_int_frac(pio, sm, div_int, div_frac); +} + +static void rp1_pio_sm_set_pins(PIO pio, uint sm, uint32_t pin_values) +{ + struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, .mask = GPIOS_MASK }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_SET_PINS, &args); +} + +static void rp1_pio_sm_set_pins_with_mask(PIO pio, uint sm, uint32_t pin_values, uint32_t pin_mask) +{ + struct rp1_pio_sm_set_pins_args args = { .sm = sm, .values = pin_values, .mask = pin_mask }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_SET_PINS, &args); +} + +static void rp1_pio_sm_set_pindirs_with_mask(PIO pio, uint sm, uint32_t pin_dirs, uint32_t pin_mask) +{ + struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = pin_dirs, .mask = pin_mask }; + + check_sm_param(sm); + valid_params_if(PIO, (pin_dirs & GPIOS_MASK) == pin_dirs); + valid_params_if(PIO, (pin_mask & pin_mask) == pin_mask); + (void)rp1_ioctl(pio, PIO_IOC_SM_SET_PINDIRS, &args); +} + +static void rp1_pio_sm_set_consecutive_pindirs(PIO pio, uint sm, uint pin_base, uint pin_count, bool is_out) +{ + uint32_t mask = ((1 << pin_count) - 1) << pin_base; + struct rp1_pio_sm_set_pindirs_args args = { .sm = sm, .dirs = is_out ? mask : 0, .mask = mask }; + + check_sm_param(sm); + valid_params_if(PIO, pin_base < RP1_PIO_GPIO_COUNT && + pin_count < RP1_PIO_GPIO_COUNT && + (pin_base + pin_count) < RP1_PIO_GPIO_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_SM_SET_PINDIRS, &args); +} + +static void rp1_pio_sm_set_enabled(PIO pio, uint sm, bool enabled) +{ + struct rp1_pio_sm_set_enabled_args args = { .mask = (1 << sm), .enable = enabled }; + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_SET_ENABLED, &args); +} + +static void rp1_pio_sm_set_enabled_mask(PIO pio, uint32_t mask, bool enabled) +{ + struct rp1_pio_sm_set_enabled_args args = { .mask = (uint16_t)mask, .enable = enabled }; + check_sm_mask(mask); + (void)rp1_ioctl(pio, PIO_IOC_SM_SET_ENABLED, &args); +} + +static void rp1_pio_sm_restart(PIO pio, uint sm) +{ + struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_RESTART, &args); +} + +static void rp1_pio_sm_restart_mask(PIO pio, uint32_t mask) +{ + struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; + check_sm_mask(mask); + (void)rp1_ioctl(pio, PIO_IOC_SM_RESTART, &args); +} + +static void rp1_pio_sm_clkdiv_restart(PIO pio, uint sm) +{ + struct rp1_pio_sm_restart_args args = { .mask = (1 << sm) }; + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_CLKDIV_RESTART, &args); +} + +static void rp1_pio_sm_clkdiv_restart_mask(PIO pio, uint32_t mask) +{ + struct rp1_pio_sm_restart_args args = { .mask = (uint16_t)mask }; + + check_sm_mask(mask); + (void)rp1_ioctl(pio, PIO_IOC_SM_CLKDIV_RESTART, &args); +} + +static void rp1_pio_sm_enable_sync(PIO pio, uint32_t mask) +{ + struct rp1_pio_sm_enable_sync_args args = { .mask = (uint16_t)mask }; + + check_sm_mask(mask); + (void)rp1_ioctl(pio, PIO_IOC_SM_ENABLE_SYNC, &args); +} + +static void rp1_pio_sm_put(PIO pio, uint sm, uint32_t data, bool blocking) +{ + struct rp1_pio_sm_put_args args = { .sm = (uint16_t)sm, .blocking = blocking, .data = data }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_PUT, &args); +} + +static uint32_t rp1_pio_sm_get(PIO pio, uint sm, bool blocking) +{ + struct rp1_pio_sm_get_args args = { .sm = (uint16_t)sm, .blocking = blocking }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_GET, &args); + return args.data; +} + +static void rp1_pio_sm_set_dmactrl(PIO pio, uint sm, bool is_tx, uint32_t ctrl) +{ + struct rp1_pio_sm_set_dmactrl_args args = { .sm = sm, .is_tx = is_tx, .ctrl = ctrl }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_SET_DMACTRL, &args); +} + +static bool rp1_pio_sm_is_rx_fifo_empty(PIO pio, uint sm) +{ + struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); + return args.empty; +} + +static bool rp1_pio_sm_is_rx_fifo_full(PIO pio, uint sm) +{ + struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); + return args.full; +} + +static uint rp1_pio_sm_get_rx_fifo_level(PIO pio, uint sm) +{ + struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = false }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); + return args.level; +} + +static bool rp1_pio_sm_is_tx_fifo_empty(PIO pio, uint sm) +{ + struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); + return args.empty; +} + +static bool rp1_pio_sm_is_tx_fifo_full(PIO pio, uint sm) +{ + struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); + return args.full; +} + +static uint rp1_pio_sm_get_tx_fifo_level(PIO pio, uint sm) +{ + struct rp1_pio_sm_fifo_state_args args = { .sm = sm, .tx = true }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_FIFO_STATE, &args); + return args.level; +} + +static void rp1_pio_sm_drain_tx_fifo(PIO pio, uint sm) +{ + struct rp1_pio_sm_clear_fifos_args args = { .sm = sm }; + + check_sm_param(sm); + (void)rp1_ioctl(pio, PIO_IOC_SM_DRAIN_TX, &args); +} + +static void rp1_smc_set_out_pins(PIO, pio_sm_config *config, uint out_base, uint out_count) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, out_base < RP1_PIO_GPIO_COUNT); + valid_params_if(PIO, out_count <= RP1_PIO_GPIO_COUNT); + c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_OUT_BASE_BITS | PROC_PIO_SM0_PINCTRL_OUT_COUNT_BITS)) | + (out_base << PROC_PIO_SM0_PINCTRL_OUT_BASE_LSB) | + (out_count << PROC_PIO_SM0_PINCTRL_OUT_COUNT_LSB); +} + +static void rp1_smc_set_set_pins(PIO, pio_sm_config *config, uint set_base, uint set_count) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, set_base < RP1_PIO_GPIO_COUNT); + valid_params_if(PIO, set_count <= 5); + c->pinctrl = (c->pinctrl & ~(PROC_PIO_SM0_PINCTRL_SET_BASE_BITS | PROC_PIO_SM0_PINCTRL_SET_COUNT_BITS)) | + (set_base << PROC_PIO_SM0_PINCTRL_SET_BASE_LSB) | + (set_count << PROC_PIO_SM0_PINCTRL_SET_COUNT_LSB); +} + + +static void rp1_smc_set_in_pins(PIO, pio_sm_config *config, uint in_base) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, in_base < RP1_PIO_GPIO_COUNT); + c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_IN_BASE_BITS) | + (in_base << PROC_PIO_SM0_PINCTRL_IN_BASE_LSB); +} + +static void rp1_smc_set_sideset_pins(PIO, pio_sm_config *config, uint sideset_base) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, sideset_base < RP1_PIO_GPIO_COUNT); + c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_BASE_BITS) | + (sideset_base << PROC_PIO_SM0_PINCTRL_SIDESET_BASE_LSB); +} + +static void rp1_smc_set_sideset(PIO, pio_sm_config *config, uint bit_count, bool optional, bool pindirs) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, bit_count <= 5); + valid_params_if(PIO, !optional || bit_count >= 1); + c->pinctrl = (c->pinctrl & ~PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_BITS) | + (bit_count << PROC_PIO_SM0_PINCTRL_SIDESET_COUNT_LSB); + + c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_SIDE_EN_BITS | PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS)) | + (bool_to_bit(optional) << PROC_PIO_SM0_EXECCTRL_SIDE_EN_LSB) | + (bool_to_bit(pindirs) << PROC_PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB); +} + +static void rp1_smc_set_clkdiv_int_frac(PIO, pio_sm_config *config, uint16_t div_int, uint8_t div_frac) +{ + smc_to_rp1(config, c); + invalid_params_if(PIO, div_int == 0 && div_frac != 0); + c->clkdiv = + (((uint)div_frac) << PROC_PIO_SM0_CLKDIV_FRAC_LSB) | + (((uint)div_int) << PROC_PIO_SM0_CLKDIV_INT_LSB); +} + +static void rp1_smc_set_clkdiv(PIO, pio_sm_config *config, float div) +{ + uint16_t div_int; + uint8_t div_frac; + rp1_pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); + sm_config_set_clkdiv_int_frac(config, div_int, div_frac); +} + +static void rp1_smc_set_wrap(PIO, pio_sm_config *config, uint wrap_target, uint wrap) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, wrap < RP1_PIO_INSTRUCTION_COUNT); + valid_params_if(PIO, wrap_target < RP1_PIO_INSTRUCTION_COUNT); + c->execctrl = (c->execctrl & ~(PROC_PIO_SM0_EXECCTRL_WRAP_TOP_BITS | PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) | + (wrap_target << PROC_PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) | + (wrap << PROC_PIO_SM0_EXECCTRL_WRAP_TOP_LSB); +} + +static void rp1_smc_set_jmp_pin(PIO, pio_sm_config *config, uint pin) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, pin < RP1_PIO_GPIO_COUNT); + c->execctrl = (c->execctrl & ~PROC_PIO_SM0_EXECCTRL_JMP_PIN_BITS) | + (pin << PROC_PIO_SM0_EXECCTRL_JMP_PIN_LSB); +} + +static void rp1_smc_set_in_shift(PIO, pio_sm_config *config, bool shift_right, bool autopush, uint push_threshold) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, push_threshold <= 32); + c->shiftctrl = (c->shiftctrl & + ~(PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS | + PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS | + PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS)) | + (bool_to_bit(shift_right) << PROC_PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB) | + (bool_to_bit(autopush) << PROC_PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB) | + ((push_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB); +} + +static void rp1_smc_set_out_shift(PIO, pio_sm_config *config, bool shift_right, bool autopull, uint pull_threshold) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, pull_threshold <= 32); + c->shiftctrl = (c->shiftctrl & + ~(PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS | + PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_BITS | + PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS)) | + (bool_to_bit(shift_right) << PROC_PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB) | + (bool_to_bit(autopull) << PROC_PIO_SM0_SHIFTCTRL_AUTOPULL_LSB) | + ((pull_threshold & 0x1fu) << PROC_PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB); +} + +static void rp1_smc_set_fifo_join(PIO, pio_sm_config *config, enum pio_fifo_join join) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, join == PIO_FIFO_JOIN_NONE || join == PIO_FIFO_JOIN_TX || join == PIO_FIFO_JOIN_RX); + c->shiftctrl = (c->shiftctrl & (uint)~(PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS | PROC_PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS)) | + (((uint)join) << PROC_PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB); +} + +static void rp1_smc_set_out_special(PIO, pio_sm_config *config, bool sticky, bool has_enable_pin, uint enable_pin_index) +{ + smc_to_rp1(config, c); + c->execctrl = (c->execctrl & + (uint)~(PROC_PIO_SM0_EXECCTRL_OUT_STICKY_BITS | PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS | + PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS)) | + (bool_to_bit(sticky) << PROC_PIO_SM0_EXECCTRL_OUT_STICKY_LSB) | + (bool_to_bit(has_enable_pin) << PROC_PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB) | + ((enable_pin_index << PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB) & PROC_PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS); +} + +static void rp1_smc_set_mov_status(PIO, pio_sm_config *config, enum pio_mov_status_type status_sel, uint status_n) +{ + smc_to_rp1(config, c); + valid_params_if(PIO, status_sel == STATUS_TX_LESSTHAN || status_sel == STATUS_RX_LESSTHAN); + c->execctrl = (c->execctrl + & ~(PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS | PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS)) + | ((((uint)status_sel) << PROC_PIO_SM0_EXECCTRL_STATUS_SEL_LSB) & PROC_PIO_SM0_EXECCTRL_STATUS_SEL_BITS) + | ((status_n << PROC_PIO_SM0_EXECCTRL_STATUS_N_LSB) & PROC_PIO_SM0_EXECCTRL_STATUS_N_BITS); +} + +static uint32_t rp1_clock_get_hz(PIO, enum clock_index clk_index) +{ + const uint32_t MHZ = 1000000; + + switch (clk_index) { + case clk_sys: + return 200 * MHZ; + default: + break; + } + return PIO_ORIGIN_ANY; +} + +static void rp1_gpio_init(PIO pio, uint gpio) +{ + struct rp1_gpio_init_args args = { .gpio = gpio }; + + valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_GPIO_INIT, &args); +} + +static void rp1_gpio_set_function(PIO pio, uint gpio, enum gpio_function fn) +{ + struct rp1_gpio_set_function_args args = { .gpio = gpio, .fn = fn }; + + valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_FUNCTION, &args); +} + +static void rp1_gpio_set_pulls(PIO pio, uint gpio, bool up, bool down) +{ + struct rp1_gpio_set_pulls_args args = { .gpio = gpio, .up = up, .down = down }; + + valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_PULLS, &args); +} + +static void rp1_gpio_set_outover(PIO pio, uint gpio, uint value) +{ + struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; + + valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_OUTOVER, &args); +} + +static void rp1_gpio_set_inover(PIO pio, uint gpio, uint value) +{ + struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; + + valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_INOVER, &args); +} + +static void rp1_gpio_set_oeover(PIO pio, uint gpio, uint value) +{ + struct rp1_gpio_set_args args = { .gpio = gpio, .value = value }; + + valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_OEOVER, &args); +} + +static void rp1_gpio_set_input_enabled(PIO pio, uint gpio, bool enabled) +{ + struct rp1_gpio_set_args args = { .gpio = gpio, .value = enabled }; + + valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_INPUT_ENABLED, &args); +} + +static void rp1_gpio_set_drive_strength(PIO pio, uint gpio, enum gpio_drive_strength drive) +{ + struct rp1_gpio_set_args args = { .gpio = gpio, .value = drive }; + + valid_params_if(PIO, gpio < RP1_PIO_GPIO_COUNT); + (void)rp1_ioctl(pio, PIO_IOC_GPIO_SET_DRIVE_STRENGTH, &args); +} + +static void rp1_pio_gpio_init(PIO pio, uint pin) +{ + valid_params_if(PIO, pin < RP1_PIO_GPIO_COUNT); + rp1_gpio_set_function(pio, pin, RP1_GPIO_FUNC_PIO); +} + +PIO rp1_create_instance(PIO_CHIP_T *chip, uint index) +{ + char pathbuf[20]; + RP1_PIO pio = NULL; + + sprintf(pathbuf, "/dev/pio%u", index); + + if (access(pathbuf, F_OK) != 0) + return NULL; + + pio = calloc(1, sizeof(*pio)); + if (!pio) + return PIO_ERR(-ENOMEM); + + pio->base.chip = chip; + pio->fd = -1; + pio->devname = strdup(pathbuf); + + rp1_pio_clear_instruction_memory(&pio->base); + + return &pio->base; +} + +int rp1_open_instance(PIO pio) +{ + RP1_PIO rp = (RP1_PIO)pio; + int fd; + + fd = open(rp->devname, O_RDWR, O_CLOEXEC); + if (fd < 0) + return -errno; + rp->fd = fd; + return 0; +} + +void rp1_close_instance(PIO pio) +{ + RP1_PIO rp = (RP1_PIO)pio; + close(rp->fd); +} + +static const PIO_CHIP_T rp1_pio_chip = { + .name = "rp1", + .compatible = "raspberrypi,rp1-pio", + .instr_count = RP1_PIO_INSTRUCTION_COUNT, + .sm_count = RP1_PIO_SM_COUNT, + .fifo_depth = 8, + + .create_instance = rp1_create_instance, + .open_instance = rp1_open_instance, + .close_instance = rp1_close_instance, + + .pio_sm_config_xfer = rp1_pio_sm_config_xfer, + .pio_sm_xfer_data = rp1_pio_sm_xfer_data, + + .pio_can_add_program_at_offset = rp1_pio_can_add_program_at_offset, + .pio_add_program_at_offset = rp1_pio_add_program_at_offset, + .pio_remove_program = rp1_pio_remove_program, + .pio_clear_instruction_memory = rp1_pio_clear_instruction_memory, + .pio_encode_delay = rp1_pio_encode_delay, + .pio_encode_sideset = rp1_pio_encode_sideset, + .pio_encode_sideset_opt = rp1_pio_encode_sideset_opt, + .pio_encode_jmp = rp1_pio_encode_jmp, + .pio_encode_jmp_not_x = rp1_pio_encode_jmp_not_x, + .pio_encode_jmp_x_dec = rp1_pio_encode_jmp_x_dec, + .pio_encode_jmp_not_y = rp1_pio_encode_jmp_not_y, + .pio_encode_jmp_y_dec = rp1_pio_encode_jmp_y_dec, + .pio_encode_jmp_x_ne_y = rp1_pio_encode_jmp_x_ne_y, + .pio_encode_jmp_pin = rp1_pio_encode_jmp_pin, + .pio_encode_jmp_not_osre = rp1_pio_encode_jmp_not_osre, + .pio_encode_wait_gpio = rp1_pio_encode_wait_gpio, + .pio_encode_wait_pin = rp1_pio_encode_wait_pin, + .pio_encode_wait_irq = rp1_pio_encode_wait_irq, + .pio_encode_in = rp1_pio_encode_in, + .pio_encode_out = rp1_pio_encode_out, + .pio_encode_push = rp1_pio_encode_push, + .pio_encode_pull = rp1_pio_encode_pull, + .pio_encode_mov = rp1_pio_encode_mov, + .pio_encode_mov_not = rp1_pio_encode_mov_not, + .pio_encode_mov_reverse = rp1_pio_encode_mov_reverse, + .pio_encode_irq_set = rp1_pio_encode_irq_set, + .pio_encode_irq_wait = rp1_pio_encode_irq_wait, + .pio_encode_irq_clear = rp1_pio_encode_irq_clear, + .pio_encode_set = rp1_pio_encode_set, + .pio_encode_nop = rp1_pio_encode_nop, + + .pio_sm_claim = rp1_pio_sm_claim, + .pio_sm_claim_mask = rp1_pio_sm_claim_mask, + .pio_sm_claim_unused = rp1_pio_sm_claim_unused, + .pio_sm_unclaim = rp1_pio_sm_unclaim, + .pio_sm_is_claimed = rp1_pio_sm_is_claimed, + + .pio_sm_init = rp1_pio_sm_init, + .pio_sm_set_config = rp1_pio_sm_set_config, + .pio_sm_exec = rp1_pio_sm_exec, + .pio_sm_clear_fifos = rp1_pio_sm_clear_fifos, + .pio_sm_set_clkdiv_int_frac = &rp1_pio_sm_set_clkdiv_int_frac, + .pio_sm_set_clkdiv = rp1_pio_sm_set_clkdiv, + .pio_sm_set_pins = rp1_pio_sm_set_pins, + .pio_sm_set_pins_with_mask = rp1_pio_sm_set_pins_with_mask, + .pio_sm_set_pindirs_with_mask = rp1_pio_sm_set_pindirs_with_mask, + .pio_sm_set_consecutive_pindirs = rp1_pio_sm_set_consecutive_pindirs, + .pio_sm_set_enabled = rp1_pio_sm_set_enabled, + .pio_sm_set_enabled_mask = rp1_pio_sm_set_enabled_mask, + .pio_sm_restart = rp1_pio_sm_restart, + .pio_sm_restart_mask = rp1_pio_sm_restart_mask, + .pio_sm_clkdiv_restart = rp1_pio_sm_clkdiv_restart, + .pio_sm_clkdiv_restart_mask = rp1_pio_sm_clkdiv_restart_mask, + .pio_sm_enable_sync = rp1_pio_sm_enable_sync, + .pio_sm_put = rp1_pio_sm_put, + .pio_sm_get = rp1_pio_sm_get, + .pio_sm_set_dmactrl = rp1_pio_sm_set_dmactrl, + .pio_sm_is_rx_fifo_empty = rp1_pio_sm_is_rx_fifo_empty, + .pio_sm_is_rx_fifo_full = rp1_pio_sm_is_rx_fifo_full, + .pio_sm_get_rx_fifo_level = rp1_pio_sm_get_rx_fifo_level, + .pio_sm_is_tx_fifo_empty = rp1_pio_sm_is_tx_fifo_empty, + .pio_sm_is_tx_fifo_full = rp1_pio_sm_is_tx_fifo_full, + .pio_sm_get_tx_fifo_level = rp1_pio_sm_get_tx_fifo_level, + .pio_sm_drain_tx_fifo = rp1_pio_sm_drain_tx_fifo, + + .pio_get_default_sm_config = rp1_pio_get_default_sm_config, + .smc_set_out_pins = rp1_smc_set_out_pins, + .smc_set_set_pins = rp1_smc_set_set_pins, + .smc_set_in_pins = rp1_smc_set_in_pins, + .smc_set_sideset_pins = rp1_smc_set_sideset_pins, + .smc_set_sideset = rp1_smc_set_sideset, + .smc_set_clkdiv_int_frac = rp1_smc_set_clkdiv_int_frac, + .smc_set_clkdiv = rp1_smc_set_clkdiv, + .smc_set_wrap = rp1_smc_set_wrap, + .smc_set_jmp_pin = rp1_smc_set_jmp_pin, + .smc_set_in_shift = rp1_smc_set_in_shift, + .smc_set_out_shift = rp1_smc_set_out_shift, + .smc_set_fifo_join = rp1_smc_set_fifo_join, + .smc_set_out_special = rp1_smc_set_out_special, + .smc_set_mov_status = rp1_smc_set_mov_status, + + .clock_get_hz = rp1_clock_get_hz, + + .pio_gpio_init = rp1_pio_gpio_init, + .gpio_init = rp1_gpio_init, + .gpio_set_function = rp1_gpio_set_function, + .gpio_set_pulls = rp1_gpio_set_pulls, + .gpio_set_outover = rp1_gpio_set_outover, + .gpio_set_inover = rp1_gpio_set_inover, + .gpio_set_oeover = rp1_gpio_set_oeover, + .gpio_set_input_enabled = rp1_gpio_set_input_enabled, + .gpio_set_drive_strength = rp1_gpio_set_drive_strength, +}; + +DECLARE_PIO_CHIP(rp1_pio_chip); diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/piolib.c b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/piolib.c new file mode 100644 index 000000000000..8d2838195521 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_pio_vendor/piolib/piolib.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023-24 Raspberry Pi Ltd. + * All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "piolib.h" +#include "piolib_priv.h" + +#define PIO_MAX_INSTANCES 4 + +extern PIO_CHIP_T *__start_piochips; +extern PIO_CHIP_T *__stop_piochips; + +static __thread PIO __pio; + +static PIO pio_instances[PIO_MAX_INSTANCES]; +static uint num_instances; +static pthread_mutex_t pio_handle_lock; + +void pio_select(PIO pio) +{ + __pio = pio; +} + +PIO pio_get_current(void) +{ + PIO pio = __pio; + check_pio_param(pio); + return pio; +} + +int pio_get_index(PIO pio) +{ + int i; + for (i = 0; i < PIO_MAX_INSTANCES; i++) + { + if (pio == pio_instances[i]) + return i; + } + return -1; +} + +int pio_init(void) +{ + static bool initialised; + const PIO_CHIP_T * const *p; + uint i = 0; + int err; + + if (initialised) + return 0; + num_instances = 0; + p = &__start_piochips; + while (p < &__stop_piochips && num_instances < PIO_MAX_INSTANCES) + { + PIO_CHIP_T *chip = *p; + PIO pio = chip->create_instance(chip, i); + if (pio && !PIO_IS_ERR(pio)) { + pio_instances[num_instances++] = pio; + i++; + } else { + p++; + i = 0; + } + } + + err = pthread_mutex_init(&pio_handle_lock, NULL); + if (err) + return err; + + initialised = true; + return 0; +} + +PIO pio_open(uint idx) +{ + PIO pio = NULL; + int err; + + err = pio_init(); + if (err) + return PIO_ERR(err); + + if (idx >= num_instances) + return PIO_ERR(-EINVAL); + + pthread_mutex_lock(&pio_handle_lock); + + pio = pio_instances[idx]; + if (pio) { + if (pio->in_use) + err = -EBUSY; + else + pio->in_use = 1; + } + + pthread_mutex_unlock(&pio_handle_lock); + + if (err) + return PIO_ERR(err); + + err = pio->chip->open_instance(pio); + if (err) { + pio->in_use = 0; + return PIO_ERR(err); + } + + pio_select(pio); + + return pio; +} + +PIO pio_open_by_name(const char *name) +{ + int err = -ENOENT; + uint i; + + err = pio_init(); + if (err) + return PIO_ERR(err); + + for (i = 0; i < num_instances; i++) { + PIO p = pio_instances[i]; + if (!strcmp(name, p->chip->name)) + break; + } + + if (i == num_instances) + return PIO_ERR(-ENOENT); + + return pio_open(i); +} + +PIO pio_open_helper(uint idx) +{ + PIO pio = pio_instances[idx]; + if (!pio || !pio->in_use) { + pio = pio_open(idx); + if (PIO_IS_ERR(pio)) { + printf("* Failed to open PIO device %d (error %d)\n", + idx, PIO_ERR_VAL(pio)); + exit(1); + } + } + return pio; +} + +void pio_close(PIO pio) +{ + pio->chip->close_instance(pio); + pthread_mutex_lock(&pio_handle_lock); + pio->in_use = 0; + pthread_mutex_unlock(&pio_handle_lock); +} + +void pio_panic(const char *msg) +{ + fprintf(stderr, "PANIC: %s\n", msg); + exit(1); +} + +void sleep_us(uint64_t us) { + const struct timespec tv = { + .tv_sec = (us / 1000000), + .tv_nsec = 1000ull * (us % 1000000) + }; + nanosleep(&tv, NULL); +} diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_rio_backend.cc b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_rio_backend.cc new file mode 100644 index 000000000000..96586dcfbecb --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_rio_backend.cc @@ -0,0 +1,896 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +#include "rp1_rio_backend.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../framebuffer-internal.h" +#include "../gpio.h" +#include "../hardware-mapping.h" + +namespace rgb_matrix { +namespace internal { +namespace { + +// Pi 5 RIO path: +// - this backend bypasses the RP1 PIO engine entirely and toggles the RP1 RIO +// GPIO fabric directly through MMIO. +// - it shares the same framebuffer data and row-order decisions as the PIO +// path, but emits clock/latch/OE transitions from the CPU instead of from a +// state machine. +// - because timing comes from busy-waits and MMIO stores, this path is opt-in +// and validates its GPIO mapping more aggressively than the generic backend. + +static const uint32_t kRioOutputPinCount = 28; +static const uint32_t kGpioFunctionSysRio = 5; +static const uint32_t kPadFastDrive = 0x15; +static const uint32_t kPadSlowDrive = 0x01; +static const off_t kPi5PeripheralBase = 0x1f000d0000ll; +static const size_t kMapSizeBytes = 0x40000; +static const uint32_t kGpioOffsetWords = 0x00000 / 4; +static const uint32_t kRioOffsetWords = 0x10000 / 4; +static const uint32_t kPadOffsetWords = 0x20000 / 4; +static const double kBaseTargetPixelClockHz = 20000000.0; + +struct GpioCtrlRegs { + volatile uint32_t status; + volatile uint32_t ctrl; +}; + +struct RioRegs { + volatile uint32_t Out; + volatile uint32_t OE; + volatile uint32_t In; + volatile uint32_t InSync; +}; + +struct RioMapCandidate { + const char *path; + off_t offset; +}; + +struct Rp1RioState { + Rp1RioState() + : active(false), + panel_init_warned(false), + map_base(NULL), + gpio_regs(NULL), + pad_regs(NULL), + rio_out(NULL), + rio_set(NULL), + rio_clr(NULL), + row_address_type(0), + double_rows(0), + parallel(0), + gpio_slowdown(1), + output_enable_bit(0), + clock_bit(0), + latch_bit(0), + used_mask(0), + word_time_nanos(0.0), + has_pending_active_words(false), + pending_addr(0), + pending_active_words(0) { + } + + bool active; + bool panel_init_warned; + // map_base spans one contiguous RP1 aperture containing GPIO control, + // RIO data registers, and pad control registers. + volatile uint32_t *map_base; + GpioCtrlRegs *gpio_regs; + volatile uint32_t *pad_regs; + RioRegs *rio_out; + RioRegs *rio_set; + RioRegs *rio_clr; + int row_address_type; + int double_rows; + int parallel; + int gpio_slowdown; + uint32_t output_enable_bit; + uint32_t clock_bit; + uint32_t latch_bit; + uint32_t used_mask; + double word_time_nanos; + bool has_pending_active_words; + uint32_t pending_addr; + int pending_active_words; + std::vector bitplane_active_words; +}; + +Rp1RioState s_rio_state; +bool s_rio_requested = false; + +static inline void ClockSetupDelay(int slowdown) { + const int loops = std::max(1, slowdown); + for (int i = 0; i < loops; ++i) { + asm volatile("nop; nop;" ::: "memory"); + } +} + +static inline void IoStoreBarrier() { +#if defined(__ARM_ARCH) && __ARM_ARCH >= 7 + asm volatile("dmb ishst" ::: "memory"); +#endif +} + +static uint64_t MonotonicNanos() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + return static_cast(ts.tv_sec) * 1000000000ull + ts.tv_nsec; +} + +static void BusyWaitNanos(uint64_t nanos) { + if (nanos == 0) return; + if (nanos <= 250) { + for (int i = 0; i < 8; ++i) { + asm volatile("nop" ::: "memory"); + } + return; + } + + const uint64_t start = MonotonicNanos(); + while ((MonotonicNanos() - start) < nanos) { + asm volatile("" ::: "memory"); + } +} + +static void BusyWaitWords(int word_count) { + if (word_count <= 0) return; + BusyWaitNanos(static_cast(word_count * s_rio_state.word_time_nanos)); +} + +static double TargetPixelClockHz() { + const int divisor = std::max(1, s_rio_state.gpio_slowdown); + return kBaseTargetPixelClockHz / divisor; +} + +static bool ReadSmallTextFile(const char *path, std::string *result) { + const int fd = open(path, O_RDONLY); + if (fd < 0) return false; + + char buffer[256]; + const ssize_t len = read(fd, buffer, sizeof(buffer) - 1); + close(fd); + if (len <= 0) return false; + + buffer[len] = '\0'; + result->assign(buffer, buffer + len); + return true; +} + +static bool FileExists(const char *path) { + struct stat st; + return stat(path, &st) == 0; +} + +static bool MappingNameSupported(const char *hardware_mapping) { + if (hardware_mapping == NULL || *hardware_mapping == '\0') { + hardware_mapping = "regular"; + } + // This list reflects the mappings we have validated against the RP1 0..27 + // GPIO range and the one-bit-per-signal constraints below. + return strcasecmp(hardware_mapping, "regular") == 0 + || strcasecmp(hardware_mapping, "classic") == 0 + || strcasecmp(hardware_mapping, "adafruit-hat") == 0 + || strcasecmp(hardware_mapping, "adafruit-hat-pwm") == 0; +} + +static uint32_t CollectColorMask(const HardwareMapping &h, int parallel) { + uint32_t mask = 0; + mask |= h.p0_r1 | h.p0_g1 | h.p0_b1 | h.p0_r2 | h.p0_g2 | h.p0_b2; + if (parallel >= 2) { + mask |= h.p1_r1 | h.p1_g1 | h.p1_b1 | h.p1_r2 | h.p1_g2 | h.p1_b2; + } + if (parallel >= 3) { + mask |= h.p2_r1 | h.p2_g1 | h.p2_b1 | h.p2_r2 | h.p2_g2 | h.p2_b2; + } + return mask; +} + +static uint32_t CollectAddressMask(const HardwareMapping &h, int double_rows, + int row_address_type) { + switch (row_address_type) { + case 0: { + uint32_t mask = h.a; + if (double_rows > 2) mask |= h.b; + if (double_rows > 4) mask |= h.c; + if (double_rows > 8) mask |= h.d; + if (double_rows > 16) mask |= h.e; + return mask; + } + case 1: + return h.a | h.b; + case 2: + return h.a | h.b | h.c | h.d; + case 3: + return h.a | h.c; + case 4: { + uint32_t mask = h.a | h.b | h.c; + if (double_rows > 8) mask |= h.d; + if (double_rows > 16) mask |= h.e; + return mask; + } + case 5: + return h.a | h.b | h.c; + default: + return 0; + } +} + +static uint32_t BuildUsedMask(const HardwareMapping &h, int double_rows, + int parallel, int row_address_type) { + uint32_t mask = h.output_enable | h.clock | h.strobe; + mask |= CollectAddressMask(h, double_rows, row_address_type); + mask |= CollectColorMask(h, parallel); + return mask; +} + +static bool FitsInRioWord(uint64_t bits) { + const uint64_t allowed_mask = (1ull << kRioOutputPinCount) - 1; + return (bits & ~allowed_mask) == 0; +} + +static bool IsSingleBit(uint64_t bits) { + return bits != 0 && (bits & (bits - 1)) == 0; +} + +static bool ValidatePinBits(const char *label, uint64_t bits) { + if (bits == 0) return true; + if (!FitsInRioWord(bits)) { + fprintf(stderr, "Pi 5-family RP1 RIO backend: %s uses GPIO bits outside 0..27.\n", + label); + return false; + } + if (!IsSingleBit(bits)) { + fprintf(stderr, + "Pi 5-family RP1 RIO backend: %s must map to exactly one GPIO pin.\n", + label); + return false; + } + return true; +} + +static bool ValidateMappingSignals(const HardwareMapping &h, int double_rows, + int parallel, int row_address_type) { + // RIO writes exact GPIO bitmasks directly. Unlike the legacy bcm GPIO path, + // there is no notion of "either of these pins is acceptable", so each signal + // used by this backend must resolve to one concrete RP1 GPIO. + if (!ValidatePinBits("clock", h.clock) + || !ValidatePinBits("strobe", h.strobe) + || !ValidatePinBits("output_enable", h.output_enable) + || !ValidatePinBits("p0_r1", h.p0_r1) + || !ValidatePinBits("p0_g1", h.p0_g1) + || !ValidatePinBits("p0_b1", h.p0_b1) + || !ValidatePinBits("p0_r2", h.p0_r2) + || !ValidatePinBits("p0_g2", h.p0_g2) + || !ValidatePinBits("p0_b2", h.p0_b2)) { + return false; + } + if (parallel >= 2 + && (!ValidatePinBits("p1_r1", h.p1_r1) + || !ValidatePinBits("p1_g1", h.p1_g1) + || !ValidatePinBits("p1_b1", h.p1_b1) + || !ValidatePinBits("p1_r2", h.p1_r2) + || !ValidatePinBits("p1_g2", h.p1_g2) + || !ValidatePinBits("p1_b2", h.p1_b2))) { + return false; + } + if (parallel >= 3 + && (!ValidatePinBits("p2_r1", h.p2_r1) + || !ValidatePinBits("p2_g1", h.p2_g1) + || !ValidatePinBits("p2_b1", h.p2_b1) + || !ValidatePinBits("p2_r2", h.p2_r2) + || !ValidatePinBits("p2_g2", h.p2_g2) + || !ValidatePinBits("p2_b2", h.p2_b2))) { + return false; + } + + if (row_address_type == 0) { + if (!ValidatePinBits("address A", h.a)) return false; + if (double_rows > 2 && !ValidatePinBits("address B", h.b)) return false; + if (double_rows > 4 && !ValidatePinBits("address C", h.c)) return false; + if (double_rows > 8 && !ValidatePinBits("address D", h.d)) return false; + if (double_rows > 16 && !ValidatePinBits("address E", h.e)) return false; + } else if (row_address_type == 1) { + if (!ValidatePinBits("address A", h.a) + || !ValidatePinBits("address B", h.b)) { + return false; + } + } else if (row_address_type == 2) { + if (!ValidatePinBits("address A", h.a) + || !ValidatePinBits("address B", h.b) + || !ValidatePinBits("address C", h.c) + || !ValidatePinBits("address D", h.d)) { + return false; + } + } else if (row_address_type == 3) { + if (!ValidatePinBits("address A", h.a) + || !ValidatePinBits("address C", h.c)) { + return false; + } + } else if (row_address_type == 4) { + if (!ValidatePinBits("address A", h.a) + || !ValidatePinBits("address B", h.b) + || !ValidatePinBits("address C", h.c)) { + return false; + } + if (double_rows > 8 && !ValidatePinBits("address D", h.d)) return false; + if (double_rows > 16 && !ValidatePinBits("address E", h.e)) return false; + } else if (row_address_type == 5) { + if (!ValidatePinBits("address A", h.a) + || !ValidatePinBits("address B", h.b) + || !ValidatePinBits("address C", h.c)) { + return false; + } + } + return true; +} + +static bool Rp1UsesShiftRegisterRowAddress(int row_address_type) { + return row_address_type == 1 || row_address_type == 3 + || row_address_type == 4 || row_address_type == 5; +} + +static bool Rp1ShiftsRowAddressEveryBitplane(int row_address_type) { + // The legacy ABC row-address setter does not cache last_row_; keep that + // timing for row-address type 3 while the other shift-register types update + // once per display row. + return row_address_type == 3; +} + +static uint32_t Rp1ShiftRegisterIdleBits(const HardwareMapping &h, int row) { + return h.a | (row == 0 ? 0 : h.b); +} + +static uint32_t Rp1AbcShiftRegisterIdleBits(const HardwareMapping &h, int row) { + return row == 0 ? h.c : 0; +} + +static uint32_t Rp1Sm5266DirectAddressBits(const HardwareMapping &h, int row) { + uint32_t bits = 0; + if (row & 0x08) bits |= h.d; + if (row & 0x10) bits |= h.e; + return bits; +} + +static uint32_t Rp1B707ShiftRegisterIdleBits(const HardwareMapping &h, + int row) { + return row == 0 ? h.c : 0; +} + +static uint32_t CalcRowAddressBits(const HardwareMapping &h, + int row_address_type, int row) { + switch (row_address_type) { + case 0: { + uint32_t bits = 0; + if (row & 0x01) bits |= h.a; + if (row & 0x02) bits |= h.b; + if (row & 0x04) bits |= h.c; + if (row & 0x08) bits |= h.d; + if (row & 0x10) bits |= h.e; + return bits; + } + case 1: + return Rp1ShiftRegisterIdleBits(h, row); + case 2: + switch (row & 0x03) { + case 0: return h.b | h.c | h.d; + case 1: return h.a | h.c | h.d; + case 2: return h.a | h.b | h.d; + default: return h.a | h.b | h.c; + } + case 3: + return Rp1AbcShiftRegisterIdleBits(h, row); + case 4: + return Rp1Sm5266DirectAddressBits(h, row); + case 5: + return Rp1B707ShiftRegisterIdleBits(h, row); + default: + return 0; + } +} + +static int DisplayRowFromLoop(int row_loop, int double_rows, int scan_mode) { + if (scan_mode != 1) return row_loop; + + const int half_double = double_rows / 2; + return (row_loop < half_double) + ? (row_loop << 1) + : (((row_loop - half_double) << 1) + 1); +} + +static void PrepareBitplaneTimings(int pwm_lsb_nanoseconds, int dither_bits, + std::vector *timings_out) { + timings_out->clear(); + int timing_ns = pwm_lsb_nanoseconds; + for (int b = 0; b < Framebuffer::kBitPlanes; ++b) { + timings_out->push_back(timing_ns); + if (b >= dither_bits) timing_ns *= 2; + } +} + +static void PrepareBitplaneActiveWords(int pwm_lsb_nanoseconds, int dither_bits, + std::vector *active_words_out) { + std::vector timings_ns; + PrepareBitplaneTimings(pwm_lsb_nanoseconds, dither_bits, &timings_ns); + + const double word_ns = 1e9 / TargetPixelClockHz(); + active_words_out->clear(); + active_words_out->reserve(timings_ns.size()); + for (size_t i = 0; i < timings_ns.size(); ++i) { + const int word_count = std::max( + 1, static_cast(ceil(static_cast(timings_ns[i]) / word_ns))); + active_words_out->push_back(word_count); + } + s_rio_state.word_time_nanos = word_ns; +} + +static volatile uint32_t *TryMmapGpio(const RioMapCandidate &candidate) { + const int fd = open(candidate.path, O_RDWR | O_SYNC); + if (fd < 0) return NULL; + + void *map = mmap(NULL, kMapSizeBytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + candidate.offset); + close(fd); + if (map == MAP_FAILED) return NULL; + return static_cast(map); +} + +static volatile uint32_t *MapGpioOrDie() { + static const RioMapCandidate kCandidates[] = { + // Pi 5 user-space notes and the bitslip reference both use gpiomem0 for + // the RP1 GPIO/RIO/PADS aperture. gpiomem4 appears suitable for generic + // line access, but not for this contiguous MMIO window. + {"/dev/gpiomem0", 0}, + {"/dev/gpiomem0", kPi5PeripheralBase}, + {"/dev/mem", kPi5PeripheralBase}, + }; + + for (size_t i = 0; i < sizeof(kCandidates) / sizeof(kCandidates[0]); ++i) { + volatile uint32_t *map = TryMmapGpio(kCandidates[i]); + if (map != NULL) return map; + } + + fprintf(stderr, + "Pi 5-family RP1 RIO backend: could not map RP1 GPIO registers via " + "/dev/gpiomem0 or /dev/mem.\n"); + abort(); +} + +static void ConfigureUsedPins() { + Rp1RioState &state = s_rio_state; + + for (uint32_t pin = 0; pin < kRioOutputPinCount; ++pin) { + const uint32_t bit = (1u << pin); + if ((state.used_mask & bit) == 0) continue; + + state.gpio_regs[pin].ctrl = kGpioFunctionSysRio; + // Clock and latch transitions are the sharpest edges in the interface, so + // keep them on a gentler pad drive than the bulk color/address outputs. + state.pad_regs[pin] = + (bit == state.clock_bit || bit == state.latch_bit) ? kPadSlowDrive + : kPadFastDrive; + state.rio_set->OE = bit; + } + + IoStoreBarrier(); + state.rio_out->Out = state.output_enable_bit; +} + +static void WriteClockedWord(uint32_t pins) { + Rp1RioState &state = s_rio_state; + // One framebuffer word corresponds to one panel clock period: present the + // GPIO data first, then raise the clock bit, then return to idle-low clock. + state.rio_out->Out = pins; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = pins | state.clock_bit; + ClockSetupDelay(state.gpio_slowdown); +} + +static void SendRawWordsBlanked(const std::vector &pins) { + if (pins.empty()) return; + + for (size_t i = 0; i < pins.size(); ++i) { + WriteClockedWord(pins[i] | s_rio_state.output_enable_bit); + } + s_rio_state.rio_out->Out = s_rio_state.output_enable_bit; +} + +static void Rp1RioShiftRegisterRowAddress(const HardwareMapping &h, + int double_rows, int row) { + Rp1RioState &state = s_rio_state; + const uint32_t blank = state.output_enable_bit; + const uint32_t clock = h.a; + const uint32_t data = h.b; + uint32_t row_bits = 0; + + for (int activate = 0; activate < double_rows; ++activate) { + state.rio_out->Out = blank; + ClockSetupDelay(state.gpio_slowdown); + + row_bits = (activate == double_rows - 1 - row) ? 0 : data; + state.rio_out->Out = blank | row_bits; + ClockSetupDelay(state.gpio_slowdown); + + state.rio_out->Out = blank | row_bits | clock; + ClockSetupDelay(state.gpio_slowdown); + } + + state.rio_out->Out = blank | row_bits; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = blank | row_bits | clock; + ClockSetupDelay(state.gpio_slowdown); +} + +static void Rp1RioShiftAbcRowAddress(const HardwareMapping &h, int double_rows, + int row) { + Rp1RioState &state = s_rio_state; + const uint32_t blank = state.output_enable_bit; + const uint32_t clock = h.a; + const uint32_t data = h.c; + uint32_t row_bits = 0; + + for (int activate = 0; activate < double_rows; ++activate) { + state.rio_out->Out = blank; + ClockSetupDelay(state.gpio_slowdown); + + row_bits = (activate == double_rows - 1 - row) ? data : 0; + state.rio_out->Out = blank | row_bits; + ClockSetupDelay(state.gpio_slowdown); + + state.rio_out->Out = blank | row_bits | clock; + ClockSetupDelay(state.gpio_slowdown); + } + + state.rio_out->Out = blank | row_bits; + ClockSetupDelay(state.gpio_slowdown); +} + +static void Rp1RioShiftSm5266RowAddress(const HardwareMapping &h, int row) { + Rp1RioState &state = s_rio_state; + const uint32_t blank = state.output_enable_bit; + const uint32_t clock = h.a; + const uint32_t data = h.b; + const uint32_t enable = h.c; + + state.rio_out->Out = blank | enable; + ClockSetupDelay(state.gpio_slowdown); + for (int r = 7; r >= 0; --r) { + const uint32_t row_bits = ((row % 8) == r) ? data : 0; + state.rio_out->Out = blank | enable | row_bits; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = blank | enable | row_bits | clock; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = blank | enable | row_bits | clock; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = blank | enable | row_bits; + ClockSetupDelay(state.gpio_slowdown); + } + + state.rio_out->Out = blank | Rp1Sm5266DirectAddressBits(h, row); + ClockSetupDelay(state.gpio_slowdown); +} + +static void Rp1RioShiftB707RowAddress(const HardwareMapping &h, int row) { + Rp1RioState &state = s_rio_state; + const uint32_t blank = state.output_enable_bit; + const uint32_t clock = h.a; + const uint32_t enable = h.b; + const uint32_t row_bits = Rp1B707ShiftRegisterIdleBits(h, row); + + state.rio_out->Out = blank | enable; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = blank | enable | row_bits; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = blank | enable | row_bits | clock; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = blank | enable | row_bits | clock; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = blank | row_bits; + ClockSetupDelay(state.gpio_slowdown); +} + +static void Rp1RioShiftRowAddress(const HardwareMapping &h, int double_rows, + int row_address_type, int row) { + switch (row_address_type) { + case 1: + Rp1RioShiftRegisterRowAddress(h, double_rows, row); + break; + case 3: + Rp1RioShiftAbcRowAddress(h, double_rows, row); + break; + case 4: + Rp1RioShiftSm5266RowAddress(h, row); + break; + case 5: + Rp1RioShiftB707RowAddress(h, row); + break; + default: + break; + } +} + +static void SendFM6126Init(const HardwareMapping &h, int columns) { + const uint32_t bits_on = CollectColorMask(h, s_rio_state.parallel) + | static_cast(h.a); + const uint32_t bits_off = h.a; + static const char *init_b12 = "0111111111111111"; + static const char *init_b13 = "0000000001000000"; + + std::vector pins; + pins.reserve(columns * 2); + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b12[i % 16] == '0') ? bits_off : bits_on; + if (i > columns - 12) value |= h.strobe; + pins.push_back(value); + } + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b13[i % 16] == '0') ? bits_off : bits_on; + if (i > columns - 13) value |= h.strobe; + pins.push_back(value); + } + SendRawWordsBlanked(pins); +} + +static void SendFM6127Init(const HardwareMapping &h, int columns) { + const uint32_t bits_r_on = h.p0_r1 | h.p0_r2; + const uint32_t bits_g_on = h.p0_g1 | h.p0_g2; + const uint32_t bits_b_on = h.p0_b1 | h.p0_b2; + const uint32_t bits_on = bits_r_on | bits_g_on | bits_b_on; + static const char *init_b12 = "1111111111001110"; + static const char *init_b13 = "1110000001100010"; + static const char *init_b11 = "0101111100000000"; + + std::vector pins; + pins.reserve(columns * 3); + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b12[i % 16] == '0') ? 0 : bits_on; + if (i > columns - 12) value |= h.strobe; + pins.push_back(value); + } + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b13[i % 16] == '0') ? 0 : bits_on; + if (i > columns - 13) value |= h.strobe; + pins.push_back(value); + } + for (int i = 0; i < columns; ++i) { + uint32_t value = (init_b11[i % 16] == '0') ? 0 : bits_on; + if (i > columns - 11) value |= h.strobe; + pins.push_back(value); + } + SendRawWordsBlanked(pins); +} + +} // namespace + +bool Rp1RioPlatformDetected() { + if (!GPIO::IsPi5Family()) { + std::string model; + if (!ReadSmallTextFile("/proc/device-tree/model", &model)) { + return false; + } + if (model.find("Raspberry Pi 5") == std::string::npos + && model.find("Compute Module 5") == std::string::npos) { + return false; + } + } + return FileExists("/dev/gpiomem0") || FileExists("/dev/mem"); +} + +void Rp1RioSetEnabled(bool enabled) { s_rio_requested = enabled; } + +bool Rp1RioBackendRequested() { return s_rio_requested; } + +bool Rp1RioShouldActivate(const char *hardware_mapping, int row_address_type, + int parallel) { + if (!s_rio_requested) return false; + if (!Rp1RioPlatformDetected()) return false; + if (!MappingNameSupported(hardware_mapping)) return false; + if (!(row_address_type >= 0 && row_address_type <= 5)) return false; + return parallel >= 1 && parallel <= 3; +} + +void Rp1RioSetGpioSlowdown(int slowdown) { + s_rio_state.gpio_slowdown = slowdown <= 1 ? 1 : slowdown; +} + +bool Rp1RioIsActive() { return s_rio_state.active; } + +void Rp1RioInitOrDie(const HardwareMapping &mapping, int double_rows, + int parallel, int pwm_lsb_nanoseconds, int dither_bits, + int row_address_type) { + Rp1RioState &state = s_rio_state; + if (state.active) return; + + state.row_address_type = row_address_type; + state.double_rows = double_rows; + state.parallel = parallel; + state.output_enable_bit = mapping.output_enable; + state.clock_bit = mapping.clock; + state.latch_bit = mapping.strobe; + state.used_mask = BuildUsedMask(mapping, double_rows, parallel, + row_address_type); + + if (!FitsInRioWord(state.used_mask)) { + fprintf(stderr, + "Pi 5-family RP1 RIO backend: the selected GPIO mapping uses pins " + "outside the RP1 0..27 GPIO range.\n"); + abort(); + } + if (!ValidateMappingSignals(mapping, double_rows, parallel, row_address_type)) { + abort(); + } + + PrepareBitplaneActiveWords(pwm_lsb_nanoseconds, dither_bits, + &state.bitplane_active_words); + + state.map_base = MapGpioOrDie(); + state.gpio_regs = + reinterpret_cast(const_cast( + state.map_base + kGpioOffsetWords)); + state.pad_regs = const_cast(state.map_base + kPadOffsetWords + 1); + state.rio_out = reinterpret_cast( + const_cast(state.map_base + kRioOffsetWords)); + state.rio_set = reinterpret_cast( + const_cast(state.map_base + kRioOffsetWords + (0x2000 / 4))); + state.rio_clr = reinterpret_cast( + const_cast(state.map_base + kRioOffsetWords + (0x3000 / 4))); + + ConfigureUsedPins(); + state.active = true; +} + +void Rp1RioInitializePanels(const HardwareMapping &mapping, + const char *panel_type, int columns) { + if (!s_rio_state.active || panel_type == NULL || *panel_type == '\0') return; + + if (strncasecmp(panel_type, "fm6126", 6) == 0) { + SendFM6126Init(mapping, columns); + } else if (strncasecmp(panel_type, "fm6127", 6) == 0) { + SendFM6127Init(mapping, columns); + } else if (!s_rio_state.panel_init_warned) { + fprintf(stderr, + "Pi 5-family RP1 RIO backend: panel init '%s' is not implemented; " + "continuing without panel init.\n", + panel_type); + s_rio_state.panel_init_warned = true; + } +} + +void Rp1RioDumpFramebuffer(Framebuffer *framebuffer, int pwm_low_bit) { + if (!s_rio_state.active || framebuffer == NULL) return; + + Rp1RioState &state = s_rio_state; + // As in the PIO path, the framebuffer already holds the per-column color + // bits. The RIO dump loop layers row addresses and panel control timing on + // top of those words and uses busy-waits to hold the PWM bitplane on-time. + const HardwareMapping &h = framebuffer->hardware_mapping(); + const int start_bit = + std::max(pwm_low_bit, Framebuffer::kBitPlanes - framebuffer->pwmbits()); + const int double_rows = framebuffer->double_rows(); + const int columns = framebuffer->columns(); + const int scan_mode = framebuffer->scan_mode(); + const bool uses_shift_register = + Rp1UsesShiftRegisterRowAddress(state.row_address_type); + const bool shifts_every_bitplane = + Rp1ShiftsRowAddressEveryBitplane(state.row_address_type); + + uint32_t previous_addr = state.has_pending_active_words + ? state.pending_addr + : CalcRowAddressBits( + h, state.row_address_type, + DisplayRowFromLoop(double_rows - 1, + double_rows, + scan_mode)); + int previous_active_words = state.has_pending_active_words + ? state.pending_active_words + : 0; + int last_shift_register_row = -1; + + for (int row_loop = 0; row_loop < double_rows; ++row_loop) { + const int display_row = + DisplayRowFromLoop(row_loop, double_rows, scan_mode); + const uint32_t current_addr = + CalcRowAddressBits(h, state.row_address_type, display_row); + + for (int bit = start_bit; bit < Framebuffer::kBitPlanes; ++bit) { + const gpio_bits_t *row_data = framebuffer->RowDataAt(display_row, bit); + + int remaining_overlap_words = previous_active_words; + for (int col = 0; col < columns; ++col) { + uint32_t pins = static_cast(row_data[col]) | previous_addr; + if (remaining_overlap_words <= 0) { + pins |= state.output_enable_bit; + } + WriteClockedWord(pins); + if (remaining_overlap_words > 0) --remaining_overlap_words; + } + + if (remaining_overlap_words > 0) { + state.rio_out->Out = previous_addr; + BusyWaitWords(remaining_overlap_words); + } + + if (uses_shift_register) { + if (shifts_every_bitplane || display_row != last_shift_register_row) { + Rp1RioShiftRowAddress(h, double_rows, state.row_address_type, + display_row); + last_shift_register_row = display_row; + } + } + + state.rio_out->Out = current_addr | state.output_enable_bit; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = + current_addr | state.output_enable_bit | state.latch_bit; + ClockSetupDelay(state.gpio_slowdown); + state.rio_out->Out = current_addr | state.output_enable_bit; + ClockSetupDelay(state.gpio_slowdown); + + previous_addr = current_addr; + previous_active_words = state.bitplane_active_words[bit]; + } + } + + // Keep the last row's final bitplane pending for the next refresh. This + // preserves the overlap pipeline across the frame boundary instead of + // displaying that row with a one-off end-of-frame dwell. + state.pending_addr = previous_addr; + state.pending_active_words = previous_active_words; + state.has_pending_active_words = true; + state.rio_out->Out = previous_addr | state.output_enable_bit; +} + +void Rp1RioDeinit() { + Rp1RioState &state = s_rio_state; + if (!state.active) return; + + state.rio_out->Out = state.output_enable_bit; + ClockSetupDelay(state.gpio_slowdown); + state.rio_clr->OE = state.used_mask; + IoStoreBarrier(); + + if (state.map_base != NULL) { + munmap(const_cast(state.map_base), kMapSizeBytes); + } + + state.active = false; + state.panel_init_warned = false; + state.map_base = NULL; + state.gpio_regs = NULL; + state.pad_regs = NULL; + state.rio_out = NULL; + state.rio_set = NULL; + state.rio_clr = NULL; + state.row_address_type = 0; + state.double_rows = 0; + state.parallel = 0; + state.gpio_slowdown = 1; + state.output_enable_bit = 0; + state.clock_bit = 0; + state.latch_bit = 0; + state.used_mask = 0; + state.word_time_nanos = 0.0; + state.has_pending_active_words = false; + state.pending_addr = 0; + state.pending_active_words = 0; + state.bitplane_active_words.clear(); +} + +} // namespace internal +} // namespace rgb_matrix diff --git a/deps/rpi-rgb-led-matrix/lib/rp1/rp1_rio_backend.h b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_rio_backend.h new file mode 100644 index 000000000000..26c6f6ed2df4 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/rp1/rp1_rio_backend.h @@ -0,0 +1,34 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +#ifndef RPI_RP1_RIO_BACKEND_H +#define RPI_RP1_RIO_BACKEND_H + +struct HardwareMapping; + +namespace rgb_matrix { +namespace internal { +class Framebuffer; + +// Internal Pi 5-family RP1 RIO renderer. +// +// This backend drives the RP1 GPIO fabric directly through MMIO. The lifecycle +// is the same as the PIO backend, with SetEnabled()/BackendRequested() carrying +// the user-facing runtime choice before activation. +bool Rp1RioPlatformDetected(); +void Rp1RioSetEnabled(bool enabled); +bool Rp1RioBackendRequested(); +bool Rp1RioShouldActivate(const char *hardware_mapping, int row_address_type, + int parallel); +void Rp1RioSetGpioSlowdown(int slowdown); +bool Rp1RioIsActive(); +void Rp1RioInitOrDie(const HardwareMapping &mapping, int double_rows, + int parallel, int pwm_lsb_nanoseconds, int dither_bits, + int row_address_type); +void Rp1RioInitializePanels(const HardwareMapping &mapping, + const char *panel_type, int columns); +void Rp1RioDumpFramebuffer(Framebuffer *framebuffer, int pwm_low_bit); +void Rp1RioDeinit(); + +} // namespace internal +} // namespace rgb_matrix + +#endif // RPI_RP1_RIO_BACKEND_H diff --git a/deps/rpi-rgb-led-matrix/lib/thread.cc b/deps/rpi-rgb-led-matrix/lib/thread.cc new file mode 100644 index 000000000000..b10767395170 --- /dev/null +++ b/deps/rpi-rgb-led-matrix/lib/thread.cc @@ -0,0 +1,100 @@ +// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- +// Copyright (C) 2013 Henner Zeller +// +// 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. +// +// 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, see + +#include "thread.h" + +#include +#include +#include +#include +#include +#include + +namespace rgb_matrix { +void *Thread::PthreadCallRun(void *tobject) { + reinterpret_cast(tobject)->Run(); + return NULL; +} + +Thread::Thread() : started_(false) {} +Thread::~Thread() { + WaitStopped(); +} + +void Thread::WaitStopped() { + if (!started_) return; + int result = pthread_join(thread_, NULL); + if (result != 0) { + perror("Issue joining thread"); + } + started_ = false; +} + +void Thread::Start(int priority, uint32_t affinity_mask) { + assert(!started_); // Did you call WaitStopped() ? + pthread_create(&thread_, NULL, &PthreadCallRun, this); + int err; + + if (priority > 0) { + struct sched_param p; + p.sched_priority = priority; + if ((err = pthread_setschedparam(thread_, SCHED_FIFO, &p))) { + char buffer[PATH_MAX]; + const char *bin = realpath("/proc/self/exe", buffer); // Linux specific. + fprintf(stderr, "Can't set realtime thread priority=%d: %s.\n" + "\tYou are probably not running as root ?\n" + "\tThis will seriously mess with color stability and flicker\n" + "\tof the matrix. Please run as `root` (e.g. by invoking this\n" + "\tprogram with `sudo`), or setting the capability on this\n" + "\tbinary by calling\n" + "\tsudo setcap 'cap_sys_nice=eip' %s\n", + p.sched_priority, strerror(err), bin ? bin : ""); + } + } + + if (affinity_mask != 0) { + cpu_set_t cpu_mask; + CPU_ZERO(&cpu_mask); + for (int i = 0; i < 32; ++i) { + if ((affinity_mask & (1< +// +// 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. +// +// 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, see +#ifndef RPI_GRAPHICS_UTF8_H +#define RPI_GRAPHICS_UTF8_H + +#include + +// Utility function that reads UTF-8 encoded codepoints from byte iterator. +// No error checking, we assume string is UTF-8 clean. +template +uint32_t utf8_next_codepoint(byte_iterator &it) { + uint32_t cp = *it++; + if (cp < 0x80) { + return cp; // iterator already incremented. + } + else if ((cp & 0xE0) == 0xC0) { + cp = ((cp & 0x1F) << 6) + (*it & 0x3F); + } + else if ((cp & 0xF0) == 0xE0) { + cp = ((cp & 0x0F) << 12) + ((*it & 0x3F) << 6); + cp += (*++it & 0x3F); + } + else if ((cp & 0xF8) == 0xF0) { + cp = ((cp & 0x07) << 18) + ((*it & 0x3F) << 12); + cp += (*++it & 0x3F) << 6; + cp += (*++it & 0x3F); + } + else if ((cp & 0xFC) == 0xF8) { + cp = ((cp & 0x03) << 24) + ((*it & 0x3F) << 18); + cp += (*++it & 0x3F) << 12; + cp += (*++it & 0x3F) << 6; + cp += (*++it & 0x3F); + } + else if ((cp & 0xFE) == 0xFC) { + cp = ((cp & 0x01) << 30) + ((*it & 0x3F) << 24); + cp += (*++it & 0x3F) << 18; + cp += (*++it & 0x3F) << 12; + cp += (*++it & 0x3F) << 6; + cp += (*++it & 0x3F); + } + ++it; + return cp; +} + +#endif // RPI_GRAPHICS_UTF8_H diff --git a/docs/hub75.md b/docs/hub75.md new file mode 100644 index 000000000000..fc366e6230ae --- /dev/null +++ b/docs/hub75.md @@ -0,0 +1,128 @@ +# HUB75 RGB LED matrix video driver + +RetroArch can render software-core video directly to HUB75 RGB LED panels +connected to a Raspberry Pi GPIO header. The driver uses the vendored +`rpi-rgb-led-matrix` library and does not require X11, Wayland, DRM, or a +desktop session. + +## Build on Raspberry Pi + +Enable the driver and build RetroArch: + +```sh +./configure --enable-hub75 +make -j4 +``` + +Select the driver in `retroarch.cfg`: + +```ini +video_driver = "hub75" +menu_driver = "rgui" +``` + +The driver uses nearest-neighbour scaling with configurable aspect handling, +supports rotation, and accepts RGB565 and XRGB8888 software frames. Hardware +rendered cores are not supported because they do not provide a CPU-readable +framebuffer. + +RetroArch input overlays are composited in software with per-pixel and global +alpha. Full-screen overlays use the complete LED canvas; non-full-screen +overlays are positioned relative to the scaled game viewport. + +## Panel configuration + +Configuration uses environment variables so panel wiring can be changed +without adding hardware-specific values to `retroarch.cfg`: + +| Variable | Meaning | +| --- | --- | +| `HUB75_ROWS` | Rows per panel (commonly 32 or 64) | +| `HUB75_COLS` | Columns per panel (commonly 64) | +| `HUB75_CHAIN` | Panels daisy-chained horizontally | +| `HUB75_PARALLEL` | Parallel output chains | +| `HUB75_BRIGHTNESS` | Brightness from 1 to 100 | +| `HUB75_SCALING` | Scaling mode: `fit`, `fill`, `stretch`, or `integer` | +| `HUB75_PWM_BITS` | PWM colour depth from 1 to 11 | +| `HUB75_GPIO_SLOWDOWN` | GPIO slowdown, usually 1-4 on newer Pi models | +| `HUB75_GPIO_MAPPING` | Pin mapping, e.g. `regular` or `adafruit-hat` | +| `HUB75_PIXEL_MAPPER` | Pixel mapper string for unusual panel layouts | +| `HUB75_RGB_SEQUENCE` | Physical channel order, e.g. `RBG` | +| `HUB75_PANEL_TYPE` | Panel initialization type such as `FM6126A` | +| `HUB75_MULTIPLEXING` | Multiplexing mapper number | +| `HUB75_ROW_ADDR_TYPE` | Row addressing type | +| `HUB75_RP1_PIO` | Set to 1 to use RP1 PIO on Raspberry Pi 5 | + +Example for one 64x64 panel: + +```sh +sudo env HUB75_ROWS=64 HUB75_COLS=64 HUB75_GPIO_SLOWDOWN=4 \ + ./retroarch -v +``` + +Scaling modes: + +- `fit` preserves the complete image and adds black bars when necessary. This + is the default. +- `fill` preserves aspect ratio and crops the image symmetrically to fill the + entire matrix. +- `stretch` uses every LED without cropping, but may distort the image. +- `integer` uses an integer enlargement factor, or samples every Nth source + pixel when the source is larger than the matrix. It keeps pixels uniform and + shows the complete image, but may leave larger black borders. + +For a single 128x64 panel on Raspberry Pi 5, `fit` preserves the complete +game image. The RP1 PIO backend is selected at runtime rather than at compile +time: + +```sh +sudo env \ + HUB75_ROWS=64 \ + HUB75_COLS=128 \ + HUB75_CHAIN=1 \ + HUB75_PARALLEL=1 \ + HUB75_RP1_PIO=1 \ + HUB75_SCALING=fit \ + ./retroarch -v +``` + +GPIO initialization normally requires root. Run RetroArch locally on the Pi; +timing-sensitive HUB75 output should not be driven through a remote GPIO +bridge. + +## Cross-compile from macOS without Docker + +With an `aarch64-linux-gnu` cross toolchain installed, the following performs +a clean build for 64-bit Raspberry Pi Linux: + +```sh +cd /path/to/RetroArch + +make clean OBJDIR_BASE=obj-linux-arm64 +make -C deps/rpi-rgb-led-matrix/lib clean +rm -f config.mk + +OS=Linux ./configure \ + --host=aarch64-linux-gnu \ + --enable-hub75 + +make -j"$(sysctl -n hw.ncpu)" \ + OBJDIR_BASE=obj-linux-arm64 + +file retroarch +``` + +The resulting executable should be reported as an `ELF 64-bit LSB` binary for +`ARM aarch64`. Raspberry Pi 5 does not require separate compile-time CPU +selection; enable its RP1 PIO backend with `HUB75_RP1_PIO=1` when launching +RetroArch on the Pi. + +Additional RetroArch features may require a Raspberry Pi sysroot. Disable +features you do not need or supply their ARM64 headers and libraries through +the toolchain/sysroot; the bundled HUB75 library itself is built with the same +`CC`, `CXX`, and `AR` selected by RetroArch. + +Setting `OS=Linux` is required when running RetroArch's configure script on +macOS; otherwise it identifies the build machine as Darwin while testing the +Linux cross compiler. A separate `OBJDIR_BASE` also prevents native macOS and +ARM64 dependency files from colliding. diff --git a/gfx/drivers/hub75_gfx.c b/gfx/drivers/hub75_gfx.c new file mode 100644 index 000000000000..f01f040b4316 --- /dev/null +++ b/gfx/drivers/hub75_gfx.c @@ -0,0 +1,973 @@ +/* RetroArch - A frontend for libretro. + * + * Raspberry Pi HUB75 RGB LED matrix video driver. + * + * RetroArch 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 3 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#ifdef HAVE_MENU +#include "../../menu/menu_driver.h" +#endif + +#include "../video_driver.h" +#include "../../configuration.h" +#include "../../driver.h" +#include "../../frontend/frontend_driver.h" +#include "../../verbosity.h" + +enum hub75_scaling_mode +{ + HUB75_SCALING_FIT = 0, + HUB75_SCALING_FILL, + HUB75_SCALING_STRETCH, + HUB75_SCALING_INTEGER +}; + +#ifdef HAVE_OVERLAY +typedef struct hub75_overlay +{ + uint32_t *pixels; + unsigned width; + unsigned height; + float tex_x; + float tex_y; + float tex_w; + float tex_h; + float vert_x; + float vert_y; + float vert_w; + float vert_h; + float alpha; + bool supports_rgba; + bool fullscreen; +} hub75_overlay_t; +#endif + +typedef struct hub75 +{ + struct RGBLedMatrix *matrix; + struct LedCanvas *canvas; + struct Color *pixels; + unsigned char *menu_frame; + size_t menu_frame_cap; + unsigned canvas_width; + unsigned canvas_height; + unsigned frame_width; + unsigned frame_height; + unsigned frame_pitch; + unsigned menu_width; + unsigned menu_height; + unsigned menu_pitch; + unsigned menu_bits; + unsigned rotation; + unsigned viewport_x; + unsigned viewport_y; + unsigned viewport_width; + unsigned viewport_height; + enum hub75_scaling_mode scaling; +#ifdef HAVE_OVERLAY + hub75_overlay_t *overlays; + unsigned overlays_size; + bool overlays_enabled; +#endif + bool rgb32; + bool menu_enabled; + bool warned_hw_frame; +} hub75_t; + +#ifdef HAVE_OVERLAY +static void hub75_overlay_free(hub75_t *hub75) +{ + unsigned i; + + if (!hub75 || !hub75->overlays) + return; + + for (i = 0; i < hub75->overlays_size; i++) + free(hub75->overlays[i].pixels); + free(hub75->overlays); + hub75->overlays = NULL; + hub75->overlays_size = 0; +} +#endif + +static enum hub75_scaling_mode hub75_get_scaling_mode(void) +{ + const char *value = getenv("HUB75_SCALING"); + + if (!value || !*value || strcmp(value, "fit") == 0) + return HUB75_SCALING_FIT; + if (strcmp(value, "fill") == 0) + return HUB75_SCALING_FILL; + if (strcmp(value, "stretch") == 0) + return HUB75_SCALING_STRETCH; + if (strcmp(value, "integer") == 0) + return HUB75_SCALING_INTEGER; + + RARCH_WARN("[HUB75] Ignoring invalid HUB75_SCALING=%s " + "(expected fit, fill, stretch, or integer).\n", value); + return HUB75_SCALING_FIT; +} + +static const char *hub75_scaling_name(enum hub75_scaling_mode scaling) +{ + switch (scaling) + { + case HUB75_SCALING_FILL: + return "fill"; + case HUB75_SCALING_STRETCH: + return "stretch"; + case HUB75_SCALING_INTEGER: + return "integer"; + default: + return "fit"; + } +} + +static int hub75_env_int(const char *name, int min_value, int max_value) +{ + const char *value = getenv(name); + char *end = NULL; + long parsed; + + if (!value || !*value) + return 0; + + errno = 0; + parsed = strtol(value, &end, 10); + if (errno || end == value || *end || parsed < min_value || parsed > max_value) + { + RARCH_WARN("[HUB75] Ignoring invalid %s=%s (expected %d..%d).\n", + name, value, min_value, max_value); + return 0; + } + + return (int)parsed; +} + +static void hub75_input_driver(const char *joypad_driver, + input_driver_t **input, void **input_data) +{ +#ifdef HAVE_UDEV + *input_data = input_driver_init_wrap(&input_udev, joypad_driver); + if (*input_data) + { + *input = &input_udev; + return; + } +#else + (void)joypad_driver; +#endif + *input = NULL; + *input_data = NULL; +} + +static void hub75_free(void *data) +{ + hub75_t *hub75 = (hub75_t*)data; + + if (!hub75) + return; + + if (hub75->canvas) + led_canvas_clear(hub75->canvas); + if (hub75->matrix) + led_matrix_delete(hub75->matrix); + +#ifdef HAVE_OVERLAY + hub75_overlay_free(hub75); +#endif + free(hub75->menu_frame); + free(hub75->pixels); + free(hub75); +} + +static void *hub75_init(const video_info_t *video, + input_driver_t **input, void **input_data) +{ + settings_t *settings = config_get_ptr(); + hub75_t *hub75 = (hub75_t*)calloc(1, sizeof(*hub75)); + struct RGBLedMatrixOptions options; + struct RGBLedRuntimeOptions runtime; + int width = 0; + int height = 0; + + *input = NULL; + *input_data = NULL; + if (!hub75) + return NULL; + + memset(&options, 0, sizeof(options)); + memset(&runtime, 0, sizeof(runtime)); + + options.hardware_mapping = getenv("HUB75_GPIO_MAPPING"); + options.rows = hub75_env_int("HUB75_ROWS", 8, 128); + options.cols = hub75_env_int("HUB75_COLS", 8, 256); + options.chain_length = hub75_env_int("HUB75_CHAIN", 1, 64); + options.parallel = hub75_env_int("HUB75_PARALLEL", 1, 6); + options.pwm_bits = hub75_env_int("HUB75_PWM_BITS", 1, 11); + options.brightness = hub75_env_int("HUB75_BRIGHTNESS", 1, 100); + options.multiplexing = hub75_env_int("HUB75_MULTIPLEXING", 0, 32); + options.row_address_type = hub75_env_int("HUB75_ROW_ADDR_TYPE", 0, 5); + options.led_rgb_sequence = getenv("HUB75_RGB_SEQUENCE"); + options.pixel_mapper_config = getenv("HUB75_PIXEL_MAPPER"); + options.panel_type = getenv("HUB75_PANEL_TYPE"); + runtime.gpio_slowdown = hub75_env_int("HUB75_GPIO_SLOWDOWN", 0, 10); + runtime.rp1_pio = hub75_env_int("HUB75_RP1_PIO", 0, 1); + runtime.daemon = 0; + runtime.drop_privileges = 0; + runtime.do_gpio_init = true; + + hub75->matrix = led_matrix_create_from_options_and_rt_options( + &options, &runtime); + if (!hub75->matrix) + { + RARCH_ERR("[HUB75] Failed to initialize the RGB LED matrix.\n"); + hub75_free(hub75); + return NULL; + } + + hub75->canvas = led_matrix_create_offscreen_canvas(hub75->matrix); + if (!hub75->canvas) + { + RARCH_ERR("[HUB75] Failed to create an offscreen canvas.\n"); + hub75_free(hub75); + return NULL; + } + + led_canvas_get_size(hub75->canvas, &width, &height); + if (width <= 0 || height <= 0 || + (size_t)width > SIZE_MAX / (size_t)height / sizeof(*hub75->pixels)) + { + RARCH_ERR("[HUB75] Invalid matrix geometry %dx%d.\n", width, height); + hub75_free(hub75); + return NULL; + } + + hub75->pixels = (struct Color*)calloc( + (size_t)width * (size_t)height, sizeof(*hub75->pixels)); + if (!hub75->pixels) + { + hub75_free(hub75); + return NULL; + } + + hub75->canvas_width = (unsigned)width; + hub75->canvas_height = (unsigned)height; + hub75->viewport_width = hub75->canvas_width; + hub75->viewport_height = hub75->canvas_height; + hub75->frame_width = video->width; + hub75->frame_height = video->height; + hub75->frame_pitch = video->width * (video->rgb32 ? 4 : 2); + hub75->rgb32 = video->rgb32; + hub75->menu_enabled = true; + hub75->scaling = hub75_get_scaling_mode(); + + hub75_input_driver(settings->arrays.input_joypad_driver, input, input_data); + frontend_driver_install_signal_handler(); + led_canvas_clear(hub75->canvas); + hub75->canvas = led_matrix_swap_on_vsync(hub75->matrix, hub75->canvas); + + RARCH_LOG("[HUB75] Initialized %ux%u RGB LED matrix (scaling: %s).\n", + hub75->canvas_width, hub75->canvas_height, + hub75_scaling_name(hub75->scaling)); + return hub75; +} + +static struct Color hub75_read_pixel(const void *frame, unsigned pitch, + unsigned x, unsigned y, unsigned bits, bool menu) +{ + struct Color color; + + if (bits == 32) + { + const uint32_t *row = (const uint32_t*)((const uint8_t*)frame + + (size_t)y * pitch); + uint32_t pixel = row[x]; + color.r = (uint8_t)(pixel >> 16); + color.g = (uint8_t)(pixel >> 8); + color.b = (uint8_t)pixel; + } + else + { + const uint16_t *row = (const uint16_t*)((const uint8_t*)frame + + (size_t)y * pitch); + uint16_t pixel = row[x]; + if (menu) + { + color.r = (uint8_t)(((pixel >> 12) & 0xf) * 17); + color.g = (uint8_t)(((pixel >> 8) & 0xf) * 17); + color.b = (uint8_t)(((pixel >> 4) & 0xf) * 17); + } + else + { + unsigned r = (pixel >> 11) & 0x1f; + unsigned g = (pixel >> 5) & 0x3f; + unsigned b = pixel & 0x1f; + color.r = (uint8_t)((r << 3) | (r >> 2)); + color.g = (uint8_t)((g << 2) | (g >> 4)); + color.b = (uint8_t)((b << 3) | (b >> 2)); + } + } + + return color; +} + +#ifdef HAVE_OVERLAY +static void hub75_render_overlays(hub75_t *hub75) +{ + unsigned i; + + if (!hub75 || !hub75->overlays_enabled || !hub75->overlays) + return; + + for (i = 0; i < hub75->overlays_size; i++) + { + hub75_overlay_t *overlay = &hub75->overlays[i]; + unsigned base_x; + unsigned base_y; + unsigned base_width; + unsigned base_height; + int dst_x; + int dst_y; + int dst_width; + int dst_height; + int start_x; + int start_y; + int end_x; + int end_y; + int x; + int y; + + if (!overlay->pixels || !overlay->width || !overlay->height || + overlay->alpha <= 0.0f || overlay->tex_w <= 0.0f || + overlay->tex_h <= 0.0f) + continue; + + if (overlay->fullscreen) + { + base_x = 0; + base_y = 0; + base_width = hub75->canvas_width; + base_height = hub75->canvas_height; + } + else + { + base_x = hub75->viewport_x; + base_y = hub75->viewport_y; + base_width = hub75->viewport_width; + base_height = hub75->viewport_height; + } + + dst_x = (int)base_x + (int)(overlay->vert_x * base_width); + dst_y = (int)base_y + (int)(overlay->vert_y * base_height); + dst_width = (int)(overlay->vert_w * base_width + 0.5f); + dst_height = (int)(overlay->vert_h * base_height + 0.5f); + if (dst_width <= 0 || dst_height <= 0) + continue; + + start_x = dst_x < 0 ? 0 : dst_x; + start_y = dst_y < 0 ? 0 : dst_y; + end_x = dst_x + dst_width; + end_y = dst_y + dst_height; + if (end_x > (int)hub75->canvas_width) + end_x = (int)hub75->canvas_width; + if (end_y > (int)hub75->canvas_height) + end_y = (int)hub75->canvas_height; + + for (y = start_y; y < end_y; y++) + { + float v = overlay->tex_y + + ((float)(y - dst_y) / dst_height) * overlay->tex_h; + int source_y = (int)(v * overlay->height); + + if (source_y < 0) + source_y = 0; + else if (source_y >= (int)overlay->height) + source_y = (int)overlay->height - 1; + + for (x = start_x; x < end_x; x++) + { + float u = overlay->tex_x + + ((float)(x - dst_x) / dst_width) * overlay->tex_w; + int source_x = (int)(u * overlay->width); + uint32_t pixel; + unsigned alpha; + unsigned red; + unsigned green; + unsigned blue; + struct Color *destination; + + if (source_x < 0) + source_x = 0; + else if (source_x >= (int)overlay->width) + source_x = (int)overlay->width - 1; + + pixel = overlay->pixels[(size_t)source_y * overlay->width + + (unsigned)source_x]; + alpha = (unsigned)(((pixel >> 24) & 0xff) * overlay->alpha); + if (!alpha) + continue; + if (alpha > 255) + alpha = 255; + + green = (pixel >> 8) & 0xff; + if (overlay->supports_rgba) + { + red = pixel & 0xff; + blue = (pixel >> 16) & 0xff; + } + else + { + red = (pixel >> 16) & 0xff; + blue = pixel & 0xff; + } + + destination = &hub75->pixels[(size_t)y * hub75->canvas_width + + (unsigned)x]; + destination->r = (uint8_t)((red * alpha + + destination->r * (255 - alpha) + 127) / 255); + destination->g = (uint8_t)((green * alpha + + destination->g * (255 - alpha) + 127) / 255); + destination->b = (uint8_t)((blue * alpha + + destination->b * (255 - alpha) + 127) / 255); + } + } + } +} +#endif + +static void hub75_render(hub75_t *hub75, const void *frame, + unsigned width, unsigned height, unsigned pitch, unsigned bits, + bool menu, bool render_overlays) +{ + unsigned rotated_width = (hub75->rotation & 1) ? height : width; + unsigned rotated_height = (hub75->rotation & 1) ? width : height; + unsigned output_width = hub75->canvas_width; + unsigned output_height = hub75->canvas_height; + unsigned draw_width; + unsigned draw_height; + unsigned offset_x; + unsigned offset_y; + unsigned crop_x = 0; + unsigned crop_y = 0; + unsigned crop_width = rotated_width; + unsigned crop_height = rotated_height; + unsigned integer_factor = 1; + bool integer_downscale = false; + unsigned x; + unsigned y; + + memset(hub75->pixels, 0, (size_t)output_width * output_height * + sizeof(*hub75->pixels)); + + draw_width = output_width; + draw_height = output_height; + offset_x = 0; + offset_y = 0; + + if (hub75->scaling == HUB75_SCALING_FIT) + { + if ((uint64_t)output_width * rotated_height <= + (uint64_t)output_height * rotated_width) + { + draw_width = output_width; + draw_height = (unsigned)((uint64_t)rotated_height * output_width / + rotated_width); + } + else + { + draw_height = output_height; + draw_width = (unsigned)((uint64_t)rotated_width * output_height / + rotated_height); + } + + if (!draw_width) + draw_width = 1; + if (!draw_height) + draw_height = 1; + offset_x = (output_width - draw_width) / 2; + offset_y = (output_height - draw_height) / 2; + } + else if (hub75->scaling == HUB75_SCALING_FILL) + { + if ((uint64_t)output_width * rotated_height >= + (uint64_t)output_height * rotated_width) + { + crop_height = (unsigned)((uint64_t)rotated_width * output_height / + output_width); + if (!crop_height) + crop_height = 1; + crop_y = (rotated_height - crop_height) / 2; + } + else + { + crop_width = (unsigned)((uint64_t)rotated_height * output_width / + output_height); + if (!crop_width) + crop_width = 1; + crop_x = (rotated_width - crop_width) / 2; + } + } + else if (hub75->scaling == HUB75_SCALING_INTEGER) + { + if (rotated_width <= output_width && rotated_height <= output_height) + { + unsigned scale_x = output_width / rotated_width; + unsigned scale_y = output_height / rotated_height; + integer_factor = scale_x < scale_y ? scale_x : scale_y; + if (!integer_factor) + integer_factor = 1; + draw_width = rotated_width * integer_factor; + draw_height = rotated_height * integer_factor; + } + else + { + unsigned divisor_x = (rotated_width + output_width - 1) / + output_width; + unsigned divisor_y = (rotated_height + output_height - 1) / + output_height; + integer_factor = divisor_x > divisor_y ? divisor_x : divisor_y; + integer_downscale = true; + draw_width = (rotated_width + integer_factor - 1) / integer_factor; + draw_height = (rotated_height + integer_factor - 1) / integer_factor; + } + offset_x = (output_width - draw_width) / 2; + offset_y = (output_height - draw_height) / 2; + } + + if (!menu) + { + hub75->viewport_x = offset_x; + hub75->viewport_y = offset_y; + hub75->viewport_width = draw_width; + hub75->viewport_height = draw_height; + } + + for (y = 0; y < draw_height; y++) + { + unsigned ry = hub75->scaling == HUB75_SCALING_INTEGER + ? (integer_downscale ? y * integer_factor : y / integer_factor) + : crop_y + (unsigned)((uint64_t)y * crop_height / draw_height); + for (x = 0; x < draw_width; x++) + { + unsigned rx = hub75->scaling == HUB75_SCALING_INTEGER + ? (integer_downscale ? x * integer_factor : x / integer_factor) + : crop_x + (unsigned)((uint64_t)x * crop_width / draw_width); + unsigned sx; + unsigned sy; + + switch (hub75->rotation & 3) + { + case 1: + sx = ry; + sy = height - 1 - rx; + break; + case 2: + sx = width - 1 - rx; + sy = height - 1 - ry; + break; + case 3: + sx = width - 1 - ry; + sy = rx; + break; + default: + sx = rx; + sy = ry; + break; + } + + hub75->pixels[(size_t)(offset_y + y) * output_width + offset_x + x] = + hub75_read_pixel(frame, pitch, sx, sy, bits, menu); + } + } + +#ifdef HAVE_OVERLAY + if (render_overlays) + hub75_render_overlays(hub75); +#else + (void)render_overlays; +#endif + + led_canvas_set_pixels(hub75->canvas, 0, 0, + (int)output_width, (int)output_height, hub75->pixels); + hub75->canvas = led_matrix_swap_on_vsync(hub75->matrix, hub75->canvas); +} + +static bool hub75_frame(void *data, const void *frame, + unsigned frame_width, unsigned frame_height, uint64_t frame_count, + unsigned pitch, const char *msg, video_frame_info_t *video_info) +{ + hub75_t *hub75 = (hub75_t*)data; + const void *source = frame; + unsigned width; + unsigned height; + unsigned bits; + bool menu = false; + bool render_overlays = true; +#ifdef HAVE_MENU + bool menu_alive = video_info && + (video_info->menu_st_flags & MENU_ST_FLAG_ALIVE); + menu_driver_frame(menu_alive, video_info); +#else + (void)video_info; +#endif + (void)frame_count; + (void)msg; + + if (!hub75 || !frame || !frame_width || !frame_height) + return true; + + if (frame == RETRO_HW_FRAME_BUFFER_VALID) + { + if (!hub75->warned_hw_frame) + { + RARCH_WARN("[HUB75] Hardware-rendered frames are not supported.\n"); + hub75->warned_hw_frame = true; + } + return true; + } + + if (frame_width > 4 && frame_height > 4) + { + hub75->frame_width = frame_width; + hub75->frame_height = frame_height; + hub75->frame_pitch = pitch; + } + +#ifdef HAVE_MENU + if (hub75->menu_enabled && menu_alive && hub75->menu_frame) + { + source = hub75->menu_frame; + width = hub75->menu_width; + height = hub75->menu_height; + pitch = hub75->menu_pitch; + bits = hub75->menu_bits; + menu = true; + if (video_info && video_info->overlay_behind_menu) + render_overlays = false; + } + else +#endif + { + if (frame_width == 4 && frame_height == 4) + return true; + width = hub75->frame_width; + height = hub75->frame_height; + pitch = hub75->frame_pitch; + bits = hub75->rgb32 ? 32 : 16; + } + + if (source && width && height && pitch) + hub75_render(hub75, source, width, height, pitch, bits, menu, + render_overlays); + return true; +} + +static void hub75_set_nonblock_state(void *data, bool toggle, + bool adaptive_vsync_enabled, unsigned swap_interval) +{ + (void)data; + (void)toggle; + (void)adaptive_vsync_enabled; + (void)swap_interval; +} + +static bool hub75_alive(void *data) +{ + (void)data; + return !frontend_driver_get_signal_handler_state(); +} + +static bool hub75_focus(void *data) { (void)data; return true; } +static bool hub75_suppress_screensaver(void *data, bool enable) +{ (void)data; (void)enable; return false; } +static bool hub75_has_windowed(void *data) { (void)data; return false; } +static bool hub75_set_shader(void *data, enum rarch_shader_type type, + const char *path) +{ (void)data; (void)type; (void)path; return false; } +static void hub75_set_viewport(void *data, unsigned width, unsigned height, + bool force_full, bool allow_rotate) +{ (void)data; (void)width; (void)height; (void)force_full; (void)allow_rotate; } + +static void hub75_set_rotation(void *data, unsigned rotation) +{ + hub75_t *hub75 = (hub75_t*)data; + if (hub75) + hub75->rotation = rotation & 3; +} + +static void hub75_viewport_info(void *data, struct video_viewport *vp) +{ + hub75_t *hub75 = (hub75_t*)data; + if (!hub75 || !vp) + return; + vp->x = (int)hub75->viewport_x; + vp->y = (int)hub75->viewport_y; + vp->width = hub75->viewport_width; + vp->height = hub75->viewport_height; + vp->full_width = hub75->canvas_width; + vp->full_height = hub75->canvas_height; +} + +#ifdef HAVE_OVERLAY +static bool hub75_overlay_load(void *data, const void *image_data, + unsigned num_images) +{ + hub75_t *hub75 = (hub75_t*)data; + const struct texture_image *images = + (const struct texture_image*)image_data; + unsigned i; + + if (!hub75) + return false; + + hub75_overlay_free(hub75); + if (!num_images || !images) + return true; + + hub75->overlays = (hub75_overlay_t*)calloc(num_images, + sizeof(*hub75->overlays)); + if (!hub75->overlays) + return false; + hub75->overlays_size = num_images; + + for (i = 0; i < num_images; i++) + { + hub75_overlay_t *overlay = &hub75->overlays[i]; + size_t pixel_count; + + overlay->alpha = 1.0f; + overlay->tex_w = 1.0f; + overlay->tex_h = 1.0f; + overlay->vert_w = 1.0f; + overlay->vert_h = 1.0f; + if (!images[i].pixels || !images[i].width || !images[i].height) + continue; + if ((size_t)images[i].width > + SIZE_MAX / (size_t)images[i].height / sizeof(uint32_t)) + { + hub75_overlay_free(hub75); + return false; + } + + pixel_count = (size_t)images[i].width * images[i].height; + overlay->pixels = (uint32_t*)malloc(pixel_count * sizeof(uint32_t)); + if (!overlay->pixels) + { + hub75_overlay_free(hub75); + return false; + } + memcpy(overlay->pixels, images[i].pixels, + pixel_count * sizeof(uint32_t)); + overlay->width = images[i].width; + overlay->height = images[i].height; + overlay->supports_rgba = images[i].supports_rgba; + } + + return true; +} + +static void hub75_overlay_enable(void *data, bool state) +{ + hub75_t *hub75 = (hub75_t*)data; + if (hub75) + hub75->overlays_enabled = state; +} + +static void hub75_overlay_tex_geom(void *data, unsigned index, + float x, float y, float width, float height) +{ + hub75_t *hub75 = (hub75_t*)data; + hub75_overlay_t *overlay; + + if (!hub75 || index >= hub75->overlays_size) + return; + overlay = &hub75->overlays[index]; + overlay->tex_x = x; + overlay->tex_y = y; + overlay->tex_w = width; + overlay->tex_h = height; +} + +static void hub75_overlay_vertex_geom(void *data, unsigned index, + float x, float y, float width, float height) +{ + hub75_t *hub75 = (hub75_t*)data; + hub75_overlay_t *overlay; + + if (!hub75 || index >= hub75->overlays_size) + return; + overlay = &hub75->overlays[index]; + overlay->vert_x = x; + overlay->vert_y = y; + overlay->vert_w = width; + overlay->vert_h = height; +} + +static void hub75_overlay_full_screen(void *data, bool enable) +{ + hub75_t *hub75 = (hub75_t*)data; + unsigned i; + + if (!hub75) + return; + for (i = 0; i < hub75->overlays_size; i++) + hub75->overlays[i].fullscreen = enable; +} + +static void hub75_overlay_set_alpha(void *data, unsigned index, float mod) +{ + hub75_t *hub75 = (hub75_t*)data; + if (!hub75 || index >= hub75->overlays_size) + return; + if (mod < 0.0f) + mod = 0.0f; + else if (mod > 1.0f) + mod = 1.0f; + hub75->overlays[index].alpha = mod; +} + +static const video_overlay_interface_t hub75_overlay_interface = { + hub75_overlay_enable, + hub75_overlay_load, + hub75_overlay_tex_geom, + hub75_overlay_vertex_geom, + hub75_overlay_full_screen, + hub75_overlay_set_alpha +}; + +static void hub75_get_overlay_interface(void *data, + const video_overlay_interface_t **iface) +{ + (void)data; + *iface = &hub75_overlay_interface; +} +#endif + +#ifdef HAVE_MENU +static void hub75_set_texture_frame(void *data, const void *frame, bool rgb32, + unsigned width, unsigned height, float alpha) +{ + hub75_t *hub75 = (hub75_t*)data; + unsigned pitch = width * (rgb32 ? 4 : 2); + size_t required; + unsigned char *new_frame; + (void)alpha; + + if (!hub75 || !frame || !width || !height) + return; + if ((size_t)height > SIZE_MAX / pitch) + return; + required = (size_t)pitch * height; + if (required > hub75->menu_frame_cap) + { + new_frame = (unsigned char*)realloc(hub75->menu_frame, required); + if (!new_frame) + return; + hub75->menu_frame = new_frame; + hub75->menu_frame_cap = required; + } + memcpy(hub75->menu_frame, frame, required); + hub75->menu_width = width; + hub75->menu_height = height; + hub75->menu_pitch = pitch; + hub75->menu_bits = rgb32 ? 32 : 16; +} + +static void hub75_set_texture_enable(void *data, bool enable, bool full_screen) +{ + hub75_t *hub75 = (hub75_t*)data; + (void)full_screen; + if (hub75) + hub75->menu_enabled = enable; +} +#endif + +static const video_poke_interface_t hub75_poke_interface = { + NULL, /* get_flags */ + NULL, /* load_texture */ + NULL, /* unload_texture */ + NULL, /* set_video_mode */ + NULL, /* get_refresh_rate */ + NULL, /* set_filtering */ + NULL, /* get_video_output_size */ + NULL, /* get_video_output_prev */ + NULL, /* get_video_output_next */ + NULL, /* get_current_framebuffer */ + NULL, /* get_proc_address */ + NULL, /* set_aspect_ratio */ + NULL, /* apply_state_changes */ +#ifdef HAVE_MENU + hub75_set_texture_frame, + hub75_set_texture_enable, +#else + NULL, /* set_texture_frame */ + NULL, /* set_texture_enable */ +#endif + NULL, /* set_osd_msg */ + NULL, /* show_mouse */ + NULL, /* grab_mouse_toggle */ + NULL, /* get_current_shader */ + NULL, /* get_current_software_framebuffer */ + NULL, /* get_hw_render_interface */ + NULL, /* set_hdr_menu_nits */ + NULL, /* set_hdr_paper_white_nits */ + NULL, /* set_hdr_expand_gamut */ + NULL, /* set_hdr_scanlines */ + NULL, /* set_hdr_subpixel_layout */ + NULL, /* supports_texture_format */ + NULL /* load_texture_compressed */ +}; + +static void hub75_get_poke_interface(void *data, + const video_poke_interface_t **iface) +{ + (void)data; + *iface = &hub75_poke_interface; +} + +video_driver_t video_hub75 = { + hub75_init, + hub75_frame, + hub75_set_nonblock_state, + hub75_alive, + hub75_focus, + hub75_suppress_screensaver, + hub75_has_windowed, + hub75_set_shader, + hub75_free, + "hub75", + hub75_set_viewport, + hub75_set_rotation, + hub75_viewport_info, + NULL, /* read_viewport */ + NULL, /* read_frame_raw */ +#ifdef HAVE_OVERLAY + hub75_get_overlay_interface, +#endif + hub75_get_poke_interface, + NULL, /* wrap_type_to_enum */ + NULL, /* shader_load_begin */ + NULL, /* shader_load_step */ +#ifdef HAVE_GFX_WIDGETS + NULL, /* gfx_widgets_enabled */ +#endif + NULL, /* invalidate_hw_render_cache */ + NULL /* read_viewport_hdr */ +}; diff --git a/gfx/video_driver.c b/gfx/video_driver.c index acb40dbfba36..12cab58fa657 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -547,6 +547,9 @@ const video_driver_t *video_drivers[] = { #endif #ifdef HAVE_NETWORK_VIDEO &video_network, +#endif +#ifdef HAVE_HUB75 + &video_hub75, #endif &video_null, NULL, diff --git a/gfx/video_driver.h b/gfx/video_driver.h index 8e930e8f16a6..8fe49e95ac8b 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -1529,6 +1529,7 @@ extern video_driver_t video_vga; extern video_driver_t video_fpga; extern video_driver_t video_sixel; extern video_driver_t video_network; +extern video_driver_t video_hub75; extern video_driver_t video_oga; extern video_driver_t video_null; diff --git a/qb/config.params.sh b/qb/config.params.sh index 8db13d9ebb5f..6a780b17eb5e 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -211,6 +211,8 @@ C89_BUILTINSPIRV_CROSS=no HAVE_METAL=no # Metal support (macOS-only) C89_METAL=no HAVE_NETWORK_VIDEO=no +HAVE_HUB75=no # Raspberry Pi HUB75 RGB LED matrix video driver +C89_HUB75=no HAVE_STEAM=no # Enable Steam build HAVE_MIST=no # Enable Steam build w/ mist HAVE_ODROIDGO2=no # ODROID-GO Advance rotation support (requires librga)