GCC Code Coverage Report


Directory: ./
File: channeloutput/BBBMatrix.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 542 682 79.5%
Functions: 17 19 89.5%
Branches: 609 1299 46.9%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the GPL v2 as described in the
10 * included LICENSE.GPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #ifdef __ARM_NEON
16 #include <arm_neon.h>
17 #endif
18
19 #include "fpp-json.h"
20
21 #include "BBBMatrix.h"
22 #include <sys/mman.h>
23 #include <sys/wait.h>
24 #include <cmath>
25 #include <fstream>
26
27 #include "../Warnings.h"
28 #include "ChannelOutputSetup.h"
29 #include "../common.h"
30 #include "../log.h"
31 #include "../settings.h"
32
33 #include "GammaLUT.h"
34 #include "PanelInterleaveHandler.h"
35 #include "overlays/PixelOverlay.h"
36 #include "util/BBBUtils.h"
37
38 #include "Plugin.h"
39 class BBBMatrixPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
40 public:
41 15 BBBMatrixPlugin() :
42
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
45 FPPPlugins::Plugin("BBBMatrix") {
43 15 }
44 15 virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
45
2/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30 return new BBBMatrix(startChannel, channelCount);
46 }
47 };
48
49 extern "C" {
50 15 FPPPlugins::Plugin* createPlugin() {
51
2/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30 return new BBBMatrixPlugin();
52 }
53 }
54
55 // These are the number of clock cycles it takes to clock out a single "row" of bits (1 bit) for 32x16 1/8 P10 scan panels. Other
56 // panel types and scan rates and stuff are proportional to these
57 static const uint32_t v1Timings[8][16] = {
58 { 0xA65, 0x14EA, 0x1F6F, 0x29ED, 0x346E, 0x3EEE, 0x496C, 0x53EB, 0x5E6B, 0x68ED, 0x7374, 0x7DF0, 0x887B, 0x92F4, 0x9D75, 0xA7FB },
59 { 0xA69, 0x14EA, 0x1F6F, 0x29ED, 0x346E, 0x3EEE, 0x496C, 0x53EB, 0x5E6B, 0x68ED, 0x7374, 0x7DF0, 0x887B, 0x92F4, 0x9D75, 0xA7FB },
60 { 0xA6A, 0x14EA, 0x1F6F, 0x29ED, 0x346E, 0x3EEE, 0x496C, 0x53EB, 0x5E6B, 0x68ED, 0x7374, 0x7DF0, 0x887B, 0x92F4, 0x9D75, 0xA7FB },
61 { 0xC88, 0x1906, 0x2588, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D },
62 { 0xC88, 0x1906, 0x2588, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D },
63 { 0xC88, 0x1906, 0x2588, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D },
64 { 0xC88, 0x1906, 0x2588, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D },
65 { 0xC90, 0x190D, 0x2590, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D }
66 };
67 static const uint32_t v2Timings[8][16] = {
68 { 0x347, 0x64F, 0x95F, 0xC70, 0xF80, 0x129E, 0x15B3, 0x18CB, 0x1BE4, 0x1EFB, 0x2211, 0x252C, 0x2786, 0x2A88, 0x2D85, 0x3088 },
69 { 0x864, 0x10ED, 0x196C, 0x21EA, 0x2A6C, 0x32E9, 0x3B6A, 0x43EC, 0x4C6A, 0x54EA, 0x5D6B, 0x65EB, 0x6E6D, 0x76ED, 0x7F6E, 0x87EE },
70 { 0x864, 0x10ED, 0x196C, 0x21EA, 0x2A6C, 0x32E9, 0x3B6A, 0x43EC, 0x4C6A, 0x54EA, 0x5D6B, 0x65EB, 0x6E6D, 0x76ED, 0x7F6E, 0x87EE },
71 { 0xA69, 0x14EA, 0x1F6F, 0x29ED, 0x346E, 0x3EEE, 0x496C, 0x53EB, 0x5E6B, 0x68ED, 0x7374, 0x7DF0, 0x887B, 0x92F4, 0x9D75, 0xA7FB },
72 { 0xA6A, 0x14EA, 0x1F6F, 0x29ED, 0x346E, 0x3EEE, 0x496C, 0x53EB, 0x5E6B, 0x68ED, 0x7374, 0x7DF0, 0x887B, 0x92F4, 0x9D75, 0xA7FB },
73 { 0xC88, 0x1906, 0x2588, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D },
74 { 0xC88, 0x1906, 0x2588, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D },
75 { 0xC88, 0x1906, 0x2588, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D },
76 };
77 static const uint32_t psTimings[8][16] = {
78 { 0x440, 0x820, 0xC30, 0x1000, 0x1400, 0x17F1, 0x1C00, 0x2000, 0x2400, 0x2800, 0x2C00, 0x3000, 0x3400, 0x3800, 0x3C00, 0x4000 },
79 { 0x940, 0x12C0, 0x1C80, 0x25C0, 0x2F40, 0x3900, 0x4240, 0x4C00, 0x5540, 0x5EC0, 0x6850, 0x71C0, 0x7B40, 0x84C0, 0x8E80, 0x97CE },
80 { 0xB70, 0x1700, 0x2280, 0x2E00, 0x3A00, 0x4500, 0x5080, 0x5C00, 0x6780, 0x7320, 0x7E80, 0x8A00, 0x9580, 0xA100, 0xAC80, 0xB7F0 },
81 { 0xB80, 0x1700, 0x2280, 0x2E00, 0x3A00, 0x4500, 0x5080, 0x5C00, 0x6780, 0x7320, 0x7E80, 0x8A00, 0x9580, 0xA100, 0xAC80, 0xB800 },
82 { 0xB90, 0x1700, 0x2280, 0x2E00, 0x3A00, 0x4500, 0x5080, 0x5C00, 0x6780, 0x7320, 0x7E80, 0x8A00, 0x9580, 0xA100, 0xAC80, 0xB800 },
83 { 0xB90, 0x1700, 0x2280, 0x2E00, 0x3A00, 0x4500, 0x5080, 0x5C00, 0x6780, 0x7320, 0x7E80, 0x8A00, 0x9580, 0xA100, 0xAC80, 0xB800 },
84 { 0xC88, 0x1906, 0x2588, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D },
85 { 0xC88, 0x1906, 0x2588, 0x340D, 0x3E8D, 0x4B10, 0x5790, 0x6410, 0x7090, 0x7D10, 0x8990, 0x9610, 0xA291, 0xAF0D, 0xBB90, 0xC80D },
86 };
87
88 static std::map<int, std::vector<int>> BIT_ORDERS = {
89 { 6, { 5, 2, 1, 4, 3, 0 } },
90 { 7, { 6, 2, 1, 4, 5, 3, 0 } },
91 { 8, { 7, 3, 5, 1, 2, 6, 4, 0 } },
92 //{ 8, { 7, 6, 5, 4, 3, 2, 1, 0 } },
93 { 9, { 8, 3, 5, 1, 7, 2, 6, 4, 0 } },
94 { 10, { 9, 4, 1, 6, 3, 8, 2, 7, 5, 0 } },
95 { 11, { 10, 4, 7, 2, 3, 1, 6, 9, 8, 5, 0 } },
96 { 12, { 11, 5, 8, 2, 4, 1, 7, 10, 3, 9, 6, 0 } }
97 };
98 14 static void compilePRUMatrixCode(std::vector<std::string>& sargs) {
99
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 CompilePRUProgram("/opt/fpp/src/pru/compileMatrix.sh", sargs);
100 14 }
101
102 14 void BBBMatrix::calcBrightnessFlags(std::vector<std::string>& sargs) {
103
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 LogDebug(VB_CHANNELOUT, "Calc Brightness: maxPanel: %d maxOutput: %d Brightness: %d rpo: %d ph: %d pw: %d\n", m_longestChain, m_outputs, m_brightness, m_panelScan, m_panelHeight, m_panelWidth);
104
105 // The tables stop at 8 outputs and 16 panels a chain, and the cape
106 // takes 16 outputs: past the tables this read beyond them. Use the
107 // nearest entry, the slowest timing the tables know.
108
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 int to = std::clamp(m_outputs, 1, 8) - 1;
109
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 int tc = std::clamp(m_longestChain, 1, 16) - 1;
110 14 uint32_t max = 0xB00;
111
1/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 switch (m_timing) {
112 ✗ case 2:
113 ✗ max = psTimings[to][tc];
114 ✗ break;
115 ✗ case 1:
116 ✗ max = v2Timings[to][tc];
117 ✗ break;
118 14 default:
119 14 max = v1Timings[to][tc];
120 14 break;
121 }
122
123 // timings are based on 32 pixel wide panels
124 14 max *= m_panelWidth;
125 14 max /= 32;
126
127 // 1/4 scan we need to double the time since we have twice the number of pixels to clock out
128 14 max *= m_panelHeight;
129 14 max /= (m_panelScan * 2);
130
131 14 uint32_t origMax = max;
132
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
14 if (m_colorDepth >= 11 && max < 0x9000) {
133 // for depth 10, we'll need a little more on time
134 // or the last bit will be on far too short
135 ✗ max = 0x9000;
136
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
14 } else if (m_colorDepth >= 10 && max < 0x8000) {
137 // for depth 10, we'll need a little more on time
138 // or the last bit will be on far too short
139 ✗ max = 0x8000;
140
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
14 } else if (m_colorDepth == 9 && max < 0x6800) {
141 // for depth 9, we'll need a little more on time
142 ✗ max = 0x6800;
143
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
14 } else if (max < 0x4500) {
144 // if max is too low, the low bit time is too short and
145 // extra ghosting occurs
146 // At this point, framerate will be supper high anyway >100fps
147 12 max = 0x4500;
148 }
149
150 14 uint32_t origMax2 = max;
151
152 14 max *= m_brightness;
153 14 max /= 10;
154
155 14 uint32_t delay = origMax2 - max;
156
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
14 if ((origMax2 > origMax) && (origMax > max)) {
157 ✗ delay = origMax - max;
158 }
159
160 14 int maxBit = 8;
161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (m_colorDepth > 8) {
162 ✗ maxBit = m_colorDepth;
163 }
164 // printf("Delay : %d Max: %d OrigMax: %d OrigMax2: %d\n", delay, max, origMax, origMax2);
165
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 14 times.
126 for (int x = 0; x < maxBit; x++) {
166
1/2
✓ Branch 0 taken 112 times.
✗ Branch 1 not taken.
112 LogDebug(VB_CHANNELOUT, "Brightness %d: %X\n", x, max);
167 112 delayValues[x] = delay;
168 112 brightnessValues[x] = max;
169 112 max >>= 1;
170 112 origMax >>= 1;
171 }
172 // low value cannot be less than 20 or ghosting
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (brightnessValues[maxBit - 1] < 20) {
174 ✗ brightnessValues[maxBit - 1] = 20;
175 }
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (brightnessValues[maxBit - 2] < brightnessValues[maxBit - 1]) {
177 ✗ brightnessValues[maxBit - 2] = brightnessValues[maxBit - 2] + 5;
178 }
179
180
3/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 13 times.
14 if (FileExists("/home/fpp/media/config/ledscape_dimming")) {
181
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 FILE* file = fopen("/home/fpp/media/config/ledscape_dimming", "r");
182
183
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (file != NULL) {
184 // fgets, not getline: getline() was handed a stack buffer as if it
185 // were malloc'd, and a line of 100 characters or more made it
186 // realloc() the stack
187 char line[100];
188 1 int count = 0;
189
190
6/8
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 1 times.
5 while ((count < 10) && fgets(line, sizeof(line), file)) {
191 4 size_t read = strlen(line);
192
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if ((!read) || (read == 1))
193 ✗ continue;
194
195
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 LogDebug(VB_CHANNELOUT, "Line %d: %s\n", count, line);
196
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (count == 0) {
197 1 m_printStats = atoi(line);
198 1 count++;
199 } else {
200 uint32_t d1, d2;
201
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (sscanf(line, "%X %X", &d1, &d2) == 2) {
202 3 brightnessValues[count - 1] = d1;
203 3 delayValues[count - 1] = d2;
204 }
205 3 count++;
206 }
207 }
208
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 fclose(file);
209 }
210 }
211
212 char buf[255];
213
214 14 int x = m_colorDepth;
215 14 m_bitOrder.clear();
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (m_outputByRow) {
217 // if outputing by row, we have to keep in decending order
218 ✗ for (int x = m_colorDepth; x > 0; --x) {
219 ✗ m_bitOrder.push_back(x - 1);
220 }
221 } else {
222
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 m_bitOrder = BIT_ORDERS[m_colorDepth];
223 }
224
225
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 14 times.
124 for (auto b : m_bitOrder) {
226 110 int idx = m_colorDepth - b - 1;
227 110 snprintf(buf, sizeof(buf), "-DBRIGHTNESS%d=%d", x, brightnessValues[idx]);
228
229
2/4
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
110 sargs.push_back(buf);
230 110 snprintf(buf, sizeof(buf), "-DDELAY%d=%d", x, delayValues[idx]);
231
2/4
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
110 sargs.push_back(buf);
232 110 x--;
233 }
234 14 }
235
236 15 BBBMatrix::BBBMatrix(unsigned int startChannel, unsigned int channelCount) :
237 ChannelOutput(startChannel, channelCount),
238 15 m_pru(nullptr),
239 15 m_pruCopy(nullptr),
240 15 m_matrix(nullptr),
241 15 m_panelMatrix(nullptr),
242 15 m_outputs(0),
243 15 m_gpioFrame(nullptr),
244 15 m_longestChain(0),
245 15 m_panelWidth(32),
246 15 m_panelHeight(16),
247 15 m_invertedData(0),
248 15 m_brightness(10),
249 15 m_colorDepth(8),
250 15 m_panelInterleave(),
251 15 m_panelScan(8),
252 15 m_timing(0),
253 15 m_printStats(false),
254 15 m_handler(nullptr),
255 15 m_outputByRow(false),
256 15 m_outputBlankData(false),
257 15 m_curFrame(0),
258 15 m_numFrames(0) {
259
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 LogDebug(VB_CHANNELOUT, "BBBMatrix::BBBMatrix(%u, %u)\n",
260 startChannel, channelCount);
261 15 }
262
263 60 BBBMatrix::~BBBMatrix() {
264 30 LogDebug(VB_CHANNELOUT, "BBBMatrix::~BBBMatrix()\n");
265
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 if (m_gpioFrame)
266
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 delete[] m_gpioFrame;
267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
30 if (m_pru)
268 ✗ delete m_pru;
269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
30 if (m_pruCopy)
270 ✗ delete m_pruCopy;
271
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 if (m_handler)
272
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 delete m_handler;
273
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 if (m_matrix)
274
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 delete m_matrix;
275
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 if (m_panelMatrix)
276
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 delete m_panelMatrix;
277 60 }
278
279 99 bool BBBMatrix::configureControlPin(const std::string& ctype, Json::Value& root, std::ofstream& outputFile, int pru, int& controlGPIO) {
280
4/8
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 99 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 99 times.
✗ Branch 7 not taken.
99 std::string type = root["controls"][ctype]["type"].asString();
281
2/4
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
✗ Branch 3 not taken.
99 if (type != "none") {
282
4/8
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 99 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 99 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 99 times.
✗ Branch 7 not taken.
99 std::string pinName = root["controls"][ctype]["pin"].asString();
283
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 m_usedGPIOS.push_back(pinName);
284
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 const PinCapabilities& pin = PinCapabilities::getPinByName(pinName);
285
5/8
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 85 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 99 times.
99 if (ctype == "oe" && pin.pwm >= 99) {
286 ✗ outputFile << "#define oe_pwm_address " << std::to_string(pin.getPWMRegisterAddress()) << "\n";
287 ✗ outputFile << "#define oe_pwm_output " << std::to_string(pin.subPwm) << "\n";
288 ✗ int max = 300 * 255;
289 // FIXME - adjust max for brightness
290 ✗ pin.setupPWM(max);
291 ✗ pin.setPWMValue(0);
292 ✗ return true;
293 } else {
294
3/4
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 97 times.
99 if (type == "pruout") {
295
4/8
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
6 pin.configPin("pru" + std::to_string(pru) + "out");
296 2 const BBBPinCapabilities* bp = static_cast<const BBBPinCapabilities*>(&pin);
297
5/10
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
2 outputFile << "#define pru_" << ctype << " " << std::to_string(bp->pruPin(pru)) << "\n";
298 } else {
299
2/4
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 97 times.
✗ Branch 3 not taken.
194 pin.configPin(type);
300
6/12
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 97 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 97 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 97 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 97 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 97 times.
✗ Branch 11 not taken.
97 outputFile << "#define gpio_" << ctype << " " << std::to_string(pin.mappedGPIO()) << "\n";
301
1/2
✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
97 controlGPIO = pin.mappedGPIOIdx();
302 }
303 }
304
1/2
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
99 }
305 99 return false;
306 99 }
307
308 102 void BBBMatrix::configurePanelPin(int x, const std::string& color, int row, Json::Value& root, std::ofstream& outputFile, int* minPort) {
309
6/12
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 102 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 102 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 102 times.
✗ Branch 11 not taken.
102 std::string pinName = root["outputs"][x]["pins"][color + std::to_string(row)].asString();
310
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 const PinCapabilities& pin = PinCapabilities::getPinByName(pinName);
311
3/6
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
408 pin.configPin();
312
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 m_usedGPIOS.push_back(pinName);
313
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 int gpioIdx = pin.mappedGPIOIdx();
314
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 minPort[gpioIdx] = std::min(minPort[gpioIdx], (int)pin.mappedGPIO());
315
8/16
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 102 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 102 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 102 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 102 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 102 times.
✗ Branch 15 not taken.
102 outputFile << "#define " << color << std::to_string(x + 1) << std::to_string(row) << "_gpio " << std::to_string(pin.mappedGPIOIdx()) << "\n";
316
8/16
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 102 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 102 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 102 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 102 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 102 times.
✗ Branch 15 not taken.
102 outputFile << "#define " << color << std::to_string(x + 1) << std::to_string(row) << "_pin " << std::to_string(pin.mappedGPIO()) << "\n";
317
318
3/4
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 68 times.
102 if (color == "r") {
319
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 m_pinInfo[x].row[row - 1].r_gpio = pin.mappedGPIOIdx();
320
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 m_pinInfo[x].row[row - 1].r_pin = 1UL << pin.mappedGPIO();
321
3/4
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 34 times.
68 } else if (color == "g") {
322
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 m_pinInfo[x].row[row - 1].g_gpio = pin.mappedGPIOIdx();
323
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 m_pinInfo[x].row[row - 1].g_pin = 1UL << pin.mappedGPIO();
324 } else {
325
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 m_pinInfo[x].row[row - 1].b_gpio = pin.mappedGPIOIdx();
326
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 m_pinInfo[x].row[row - 1].b_pin = 1UL << pin.mappedGPIO();
327 }
328 102 }
329
330 17 void BBBMatrix::configurePanelPins(int x, Json::Value& root, std::ofstream& outputFile, int* minPort) {
331
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
34 configurePanelPin(x, "r", 1, root, outputFile, minPort);
332
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
34 configurePanelPin(x, "g", 1, root, outputFile, minPort);
333
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 configurePanelPin(x, "b", 1, root, outputFile, minPort);
334 17 outputFile << "\n";
335
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
34 configurePanelPin(x, "r", 2, root, outputFile, minPort);
336
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
34 configurePanelPin(x, "g", 2, root, outputFile, minPort);
337
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 configurePanelPin(x, "b", 2, root, outputFile, minPort);
338 17 outputFile << "\n";
339 17 }
340
341 15 int BBBMatrix::Init(Json::Value config) {
342
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 LogDebug(VB_CHANNELOUT, "BBBMatrix::Init(JSON)\n");
343
344
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 m_panelWidth = config["panelWidth"].asInt();
345
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 m_panelHeight = config["panelHeight"].asInt();
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (!m_panelWidth)
347 ✗ m_panelWidth = 32;
348
349
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (!m_panelHeight)
350 ✗ m_panelHeight = 16;
351
352
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 int addressingType = config["panelRowAddressType"].asInt();
353
354
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 m_invertedData = config["invertedData"].asInt();
355
3/6
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 m_colorOrder = ColorOrderFromString(config["colorOrder"].asString());
356
357
3/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
15 m_panelMatrix = new PanelMatrix(m_panelWidth, m_panelHeight, m_invertedData);
358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (!m_panelMatrix) {
359 ✗ LogErr(VB_CHANNELOUT, "BBBMatrix: Unable to create PanelMatrix\n");
360 ✗ return 0;
361 }
362 15 bool usesOutput[16] = {
363 false, false, false, false,
364 false, false, false, false,
365 false, false, false, false,
366 false, false, false, false
367 };
368
4/6
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 15 times.
34 for (int i = 0; i < config["panels"].size(); i++) {
369
3/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
19 Json::Value p = config["panels"][i];
370 19 char orientation = 'N';
371
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
19 std::string o = p["orientation"].asString();
372
373
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (!o.empty())
374 19 orientation = o[0];
375
376
4/8
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 19 times.
✗ Branch 7 not taken.
19 if (p["colorOrder"].asString() == "")
377
3/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
19 p["colorOrder"] = ColorOrderToString(m_colorOrder);
378
379 // the cape has at most 16 outputs, and usesOutput is sized for that
380
3/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
19 if (p["outputNumber"].asInt() >= 16) {
381 ✗ LogErr(VB_CHANNELOUT, "Invalid panel output number %d\n", p["outputNumber"].asInt());
382 ✗ continue;
383 }
384 // a refused panel (out of range) must not count toward the outputs or chains below
385
4/8
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 19 times.
✗ Branch 7 not taken.
38 if (!m_panelMatrix->AddPanel(p["outputNumber"].asInt(),
386
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
19 p["panelNumber"].asInt(), orientation,
387
4/8
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 19 times.
✗ Branch 7 not taken.
19 p["xOffset"].asInt(), p["yOffset"].asInt(),
388
3/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 19 times.
38 ColorOrderFromString(p["colorOrder"].asString()))) {
389 ✗ continue;
390 }
391
392
4/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 15 times.
19 if (p["outputNumber"].asInt() > m_outputs)
393
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 m_outputs = p["outputNumber"].asInt();
394
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
19 usesOutput[p["outputNumber"].asInt()] = true;
395
396
4/6
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 17 times.
19 if (p["panelNumber"].asInt() > m_longestChain)
397
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 m_longestChain = p["panelNumber"].asInt();
398
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
19 }
399 // Both of these are 0-based, so bump them up by 1 for comparisons
400 15 m_outputs++;
401 15 m_longestChain++;
402
403 // get the dimensions of the matrix
404 15 m_panels = m_panelMatrix->PanelCount();
405 15 m_rows = m_outputs * m_panelHeight;
406 15 m_width = m_panelMatrix->Width();
407 15 m_height = m_panelMatrix->Height();
408
409
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 if (config.isMember("brightness")) {
410
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 m_brightness = config["brightness"].asInt();
411 }
412
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (m_brightness < 1 || m_brightness > 10) {
413 ✗ m_brightness = 10;
414 }
415
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 if (config.isMember("panelColorDepth")) {
416
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 m_colorDepth = config["panelColorDepth"].asInt();
417 }
418
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 if (config.isMember("panelOutputOrder")) {
419
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 m_outputByRow = config["panelOutputOrder"].asBool();
420 }
421
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (config.isMember("panelOutputBlankRow")) {
422 ✗ m_outputBlankData = config["panelOutputBlankRow"].asBool();
423 }
424
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (m_colorDepth < 0) {
425 ✗ m_colorDepth = -m_colorDepth;
426 ✗ m_outputBlankData = true;
427 }
428
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (m_colorDepth > 12 || m_colorDepth < 6) {
429 ✗ m_colorDepth = 8;
430 }
431
432
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 if (config.isMember("panelInterleave")) {
433
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 m_panelInterleave = config["panelInterleave"].asString();
434 }
435
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 m_panelScan = config["panelScan"].asInt();
436 // printf("Interleave: %d Scan: %d ZZI: %d ZZCI: %d SI: %d\n", m_interleave, m_panelScan, zigZagInterleave, zigZagClusterInterleave, stripeInterleave);
437
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (m_panelScan == 0) {
438 // default scan is 1/2 the height of the panel
439 ✗ m_panelScan = m_panelHeight / 2;
440 }
441
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 m_handler = PanelInterleaveHandler::createHandler(m_panelInterleave, m_panelWidth, m_panelHeight, m_panelScan);
442
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (!m_handler) {
443 // A panel that scans fewer rows than half its height folds the rest
444 // into extra columns, so it needs an interleave that matches; without
445 // one the pixels have nowhere to go.
446 ✗ LogErr(VB_CHANNELOUT, "BBBMatrix: Panel interleave '%s' cannot map a %dx%d 1/%d scan panel\n",
447 m_panelInterleave.c_str(), m_panelWidth, m_panelHeight, m_panelScan);
448 ✗ AddOutputConfigWarning(50, "LED panel output disabled: panel interleave '" + m_panelInterleave +
449 ✗ "' is not valid for a " + std::to_string(m_panelWidth) + "x" + std::to_string(m_panelHeight) +
450 ✗ " 1/" + std::to_string(m_panelScan) + " scan panel. Set the Panel Interleave to match the panel.");
451 ✗ return 0;
452 }
453 15 m_channelCount = m_width * m_height * 3;
454
455
3/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
15 m_matrix = new Matrix(m_startChannel, m_width, m_height);
456
457
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (config.isMember("subMatrices")) {
458 ✗ for (int i = 0; i < config["subMatrices"].size(); i++) {
459 ✗ Json::Value sm = config["subMatrices"][i];
460
461 ✗ m_matrix->AddSubMatrix(
462 ✗ sm["enabled"].asInt(),
463 ✗ sm["startChannel"].asInt() - 1,
464 ✗ sm["width"].asInt(),
465 ✗ sm["height"].asInt(),
466 ✗ sm["xOffset"].asInt(),
467 ✗ sm["yOffset"].asInt());
468 ✗ }
469 }
470
471 15 m_rowSize = m_longestChain * m_panelWidth * 3;
472 15 int maxBits = 8;
473
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (m_colorDepth > 8) {
474 ✗ maxBits = m_colorDepth;
475 }
476 15 int gpioFrameLen = m_longestChain * m_panelWidth * maxBits * (m_panelHeight / 2) * 4;
477
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 m_gpioFrame = new uint32_t[gpioFrameLen];
478 15 memset(m_gpioFrame, 0, gpioFrameLen * 4);
479
480 15 std::vector<std::string> compileArgs;
481
482
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
30 std::string dirname = "bbb";
483
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 std::string name = "Octoscroller";
484
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (getBeagleBoneType() == PocketBeagle) {
485 ✗ dirname = "pb";
486 ✗ name = "PocketScroller";
487 }
488
4/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 15 times.
15 if (config["wiringPinout"] == "v2") {
489 ✗ name += "-v2";
490 }
491
5/8
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 14 times.
15 if (config["wiringPinout"] == "v3") {
492
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 name += "-v3";
493 }
494
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 Json::Value root;
495 char filename[256];
496 15 int minPort[4] = { 99, 99, 99, 99 };
497 15 int pru = 0;
498 15 snprintf(filename, sizeof(filename), "/home/fpp/media/tmp/panels/%s.json", name.c_str());
499
3/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 14 times.
15 if (!FileExists(filename)) {
500 1 snprintf(filename, sizeof(filename), "/home/fpp/media/tmp/panels/CapePanels.json", dirname.c_str());
501
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (FileExists(filename)) {
502 ✗ name = "CapePanels";
503 }
504 }
505
3/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 14 times.
15 if (!FileExists(filename)) {
506 1 snprintf(filename, sizeof(filename), "/opt/fpp/capes/%s/panels/%s.json", dirname.c_str(), name.c_str());
507 }
508 15 bool isPWM = false;
509
3/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 14 times.
15 if (!FileExists(filename)) {
510
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogErr(VB_CHANNELOUT, "No output pin configuration for %s - %s\n", name.c_str(), filename);
511
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 AddOutputConfigWarning(50, "LED panel output: no output pin configuration for " + name);
512 1 return 0;
513 } else {
514
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 LogDebug(VB_CHANNELOUT, "Using panel pinout from %s\n", filename);
515
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
28 if (!LoadJsonFromFile(filename, root, JsonRoot::Object)) {
516 ✗ LogErr(VB_CHANNELOUT, "Could not read pin configuration for %s - %s\n", name.c_str(), filename);
517 ✗ AddOutputConfigWarning(50, "LED panel output: could not read pin configuration for " + name);
518 ✗ return 0;
519 }
520
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 std::string longName = root["longName"].asString();
521
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 LogDebug(VB_CHANNELOUT, "Using pin configuration for %s from %s\n", longName.c_str(), filename);
522
523
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 std::ofstream outputFile;
524
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile.open("/tmp/PanelPinConfiguration.hp", std::ofstream::out | std::ofstream::trunc);
525
526 // kind of a hack, ideally the timing info would go into the json as well
527
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 m_timing = root["timing"].asInt();
528
529
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 pru = root["pru"].asInt();
530
531
3/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 9 times.
14 if (root.isMember("singlePRU")) {
532
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 m_singlePRU = root["singlePRU"].asBool();
533 }
534 // m_singlePRU = true;
535
536
3/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
14 if (root.isMember("dataOffset")) {
537
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 m_dataOffset = root["dataOffset"].asInt();
538 2 m_dataOffset *= 1024; // dataOffset is in KB
539 }
540
541
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
14 int controlGpio = root["controls"]["gpio"].asInt();
542
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 configureControlPin("latch", root, outputFile, pru, controlGpio);
543
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile << "\n";
544
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 isPWM = configureControlPin("oe", root, outputFile, pru, controlGpio);
545
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile << "\n";
546
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 configureControlPin("clock", root, outputFile, pru, controlGpio);
547
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile << "\n";
548
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
28 configureControlPin("sel0", root, outputFile, pru, controlGpio);
549
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
28 configureControlPin("sel1", root, outputFile, pru, controlGpio);
550
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
28 configureControlPin("sel2", root, outputFile, pru, controlGpio);
551
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 configureControlPin("sel3", root, outputFile, pru, controlGpio);
552
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
14 if (m_panelScan > 16) {
553 // a row address of 16 or more needs the fifth "E" line - 1/32
554 // scan, and 80x40 1/20 scan (rows 16-19)
555
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 configureControlPin("sel4", root, outputFile, pru, controlGpio);
556
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 compileArgs.push_back("-DE_SCAN_LINE");
557 }
558
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile << "\n";
559
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
14 outputFile << "#define CONTROLS_GPIO_BASE GPIO" << std::to_string(controlGpio) << "\n";
560
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
14 outputFile << "#define gpio_controls_led_mask gpio" << std::to_string(controlGpio) << "_led_mask\n";
561
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile << "\n";
562
3/6
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
14 if (m_outputs > root["outputs"].size()) {
563 ✗ m_outputs = root["outputs"].size();
564 }
565
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 14 times.
126 for (int x = 0; x < 8; x++) {
566
3/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 95 times.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
112 if (usesOutput[x] && x < m_outputs) {
567
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 configurePanelPins(x, root, outputFile, minPort);
568 } else {
569
3/6
✓ Branch 0 taken 95 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 95 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 95 times.
✗ Branch 5 not taken.
95 outputFile << "#define NO_OUTPUT_" << std::to_string(x + 1) << "\n";
570
1/2
✓ Branch 0 taken 95 times.
✗ Branch 1 not taken.
95 outputFile << "\n";
571 }
572 }
573
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile << "\n";
574 #ifdef PLATFORM_BBB
575 // Dual PRU split output: the copy PRU writes up to two of the data
576 // GPIO banks itself (see FalconMatrixPRUCpy.asm), roughly a 1.35x
577 // higher pixel clock. The controls bank and GPIO0 (which has
578 // multi-microsecond write-landing jitter on the L4_WKUP bus) stay
579 // on the output PRU so their behavior is unchanged. AM335x only.
580 //
581 // Not with a GPIO clock: the clock write is posted, so the copy PRU
582 // can't write the next pixel until a read back proves the edge has
583 // landed (ACK_SPLIT), and that read makes the split slower than one
584 // PRU doing everything (measured 70Hz vs 100Hz). Without it the copy
585 // PRU's data beat ~1 in 5 clock edges and bled into the neighbouring
586 // pixel. A PRU (r30) clock is immediate and needs no read back.
587
5/10
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
14 bool gpioClock = root["controls"]["clock"]["type"].asString() != "pruout";
588
16/26
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 2 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 2 times.
✓ Branch 17 taken 12 times.
✓ Branch 18 taken 2 times.
✓ Branch 19 taken 12 times.
✓ Branch 20 taken 2 times.
✓ Branch 21 taken 12 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
14 if (!m_singlePRU && !gpioClock && !root.get("noSplitOutput", false).asBool() && getSettingInt("BBBMatrixNoSplitOutput", 0) == 0) {
589 2 int cnt = 0;
590
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for (int x = 3; x > 0; x--) { // never GPIO0
591
4/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
6 if (minPort[x] != 99 && x != controlGpio && cnt < 2) {
592 2 m_splitCpyBanks[x] = true;
593 2 cnt++;
594 }
595 }
596 2 m_splitOutput = cnt > 0;
597 }
598
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 if (m_splitOutput) {
599
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
2 LogDebug(VB_CHANNELOUT, "BBBMatrix: split output, copy PRU owns GPIO banks:%s%s%s\n",
600 m_splitCpyBanks[1] ? " 1" : "", m_splitCpyBanks[2] ? " 2" : "", m_splitCpyBanks[3] ? " 3" : "");
601 }
602 #endif
603
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 14 times.
70 for (int x = 0; x < 4; x++) {
604
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 27 times.
56 if (minPort[x] != 99) {
605
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 28 times.
29 if (x == controlGpio) {
606
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 outputFile << "#define OUTPUT_GPIO_" << std::to_string(x) << "(a, b, c, d) OUTPUT_GPIO_FORCE_CLEAR a, b, c, d\n";
607
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 26 times.
28 } else if (m_splitCpyBanks[x]) {
608 // in split mode the copy PRU owns this bank; the output
609 // PRU compiles the bank's writes out
610
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 outputFile << "#if defined(SPLIT_GPIO) && !defined(IN_CPY_PRU)\n";
611
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 outputFile << "#define OUTPUT_GPIO_" << std::to_string(x) << "(a, b, c, d)\n";
612
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 outputFile << "#else\n";
613
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 outputFile << "#define OUTPUT_GPIO_" << std::to_string(x) << "(a, b, c, d) OUTPUT_GPIO a, b, c, d\n";
614
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 outputFile << "#endif\n";
615
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 outputFile << "#define CPY_OWNS_GPIO" << std::to_string(x) << "\n";
616 } else {
617
3/6
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
26 outputFile << "#define OUTPUT_GPIO_" << std::to_string(x) << "(a, b, c, d) OUTPUT_GPIO a, b, c, d\n";
618 }
619 } else {
620
3/6
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
27 outputFile << "#define OUTPUT_GPIO_" << std::to_string(x) << "(a, b, c, d)\n";
621 }
622 }
623
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile << "\n";
624
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 if (minPort[controlGpio] == 99) {
625 // not outputting anything on the GPIO the controls are using
626 // we need to make sure the controls are set/cleared indepentent of the panel data
627
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 outputFile << "#define NO_CONTROLS_WITH_DATA\n";
628 }
629
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile << "\n";
630
631
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 outputFile.close();
632 14 }
633
634 char buf[200];
635
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 9 times.
14 if (m_singlePRU) {
636
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
10 compileArgs.push_back("-DSINGLEPRU");
637 }
638
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 if (m_splitOutput) {
639
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 compileArgs.push_back("-DSPLIT_GPIO");
640 }
641 14 snprintf(buf, sizeof(buf), "-DRUNNING_ON_PRU%d", pru);
642
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 compileArgs.push_back(buf);
643 14 snprintf(buf, sizeof(buf), "-DRUNNING_ON_PRU=%d", pru);
644
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 compileArgs.push_back(buf);
645 14 snprintf(buf, sizeof(buf), "-DOUTPUTS=%d", m_outputs);
646
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 compileArgs.push_back(buf);
647 14 snprintf(buf, sizeof(buf), "-DROWS=%d", m_panelScan);
648
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 compileArgs.push_back(buf);
649 14 snprintf(buf, sizeof(buf), "-DBITS=%d", m_colorDepth);
650
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 compileArgs.push_back(buf);
651 14 int tmp = m_longestChain * m_panelWidth;
652
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (m_panelScan * 4 == m_panelHeight) {
653 ✗ tmp *= 2;
654
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 } else if (m_panelScan * 8 == m_panelHeight) {
655 ✗ tmp *= 4;
656 }
657 14 snprintf(buf, sizeof(buf), "-DROW_LEN=%d", tmp);
658
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 compileArgs.push_back(buf);
659
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (isPWM) {
660 ✗ compileArgs.push_back("-DUSING_PWM");
661 }
662
663
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (addressingType == 2) {
664 // 1/2 scan panel that uses 2 bits, bit one for scan row 1 and bit two for row 2
665 // Normal addressing would be 1 bit, 0 for row 1, 1 for row 2
666 ✗ compileArgs.push_back("-DADDRESSING_AB=1");
667
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 } else if (addressingType == 3) {
668 // the row drivers are shift registers clocked on A with data on C;
669 // every row change clocks one bit per panel row since panels can
670 // daisy chain a driver per band of scan rows
671 ✗ compileArgs.push_back("-DADDRESSING_ABC_SHIFT=1");
672 ✗ snprintf(buf, sizeof(buf), "-DROW_CHAIN=%d", m_panelHeight);
673 ✗ compileArgs.push_back(buf);
674 }
675
676
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 calcBrightnessFlags(compileArgs);
677 // m_printStats = true;
678
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (m_printStats) {
679 ✗ snprintf(buf, sizeof(buf), "-DENABLESTATS=1", m_outputs);
680 ✗ compileArgs.push_back("-DENABLESTATS=1");
681 }
682
683
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (m_outputByRow) {
684 ✗ compileArgs.push_back("-DOUTPUTBYROW");
685 ✗ if (m_outputBlankData) {
686 ✗ compileArgs.push_back("-DOUTPUTBLANKROW");
687 }
688 } else {
689
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
28 compileArgs.push_back("-DOUTPUTBYDEPTH");
690 }
691
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 compilePRUMatrixCode(compileArgs);
692
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 std::string pru_program = "/tmp/FalconMatrix.out";
693
694
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 5 times.
14 if (!m_singlePRU) {
695
3/8
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
9 m_pruCopy = new BBBPru(pru ? 0 : 1);
696 }
697
698
3/8
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
14 m_pru = new BBBPru(pru);
699 14 m_pruData = (BBBPruMatrixData*)m_pru->data_ram;
700
701
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 5 times.
14 if (!m_singlePRU) {
702
3/6
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
27 if (!m_pruCopy->run("/tmp/FalconMatrixPRUCpy.out")) {
703 ✗ LogErr(VB_CHANNELOUT, "BBBMatrix: Unable to start PRU. May require a reboot.\n");
704 ✗ AddOutputConfigWarning(20, "BBBMatrix: Unable to start PRU. May require a reboot.");
705 ✗ return 0;
706 }
707 }
708
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
14 if (!m_pru->run(pru_program)) {
709 ✗ LogErr(VB_CHANNELOUT, "BBBMatrix: Unable to start PRU. May require a reboot.\n");
710 ✗ AddOutputConfigWarning(20, "BBBMatrix: Unable to start PRU. May require a reboot.");
711 ✗ return 0;
712 }
713
714 // Nothing may touch the PRU memories before run() has started the core.
715 // The PRUSS is only clocked once its firmware is loaded and running, so an
716 // earlier access is dropped at best and aborts the process at worst - and
717 // run() clears the whole data RAM as its last step anyway, so a write made
718 // before it could never have survived. The sibling PRU outputs
719 // (BBB48String, BBBSerial, BBShiftString, BBShiftPanel) all order it this
720 // way; this one did not, which is why a board whose PRUSS was left
721 // unpowered segfaulted here instead of reporting the error above.
722 14 m_pruData->address_dma = m_pru->ddr_addr + m_dataOffset;
723 14 m_pruData->command = 0;
724 14 m_pruData->response = 0;
725
726
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 GammaLUT::BuildForColorDepth(gammaCurve, GammaLUT::ParseConfig(config, 2.2f), m_colorDepth);
727
728
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (isPWM) {
729 // need to calculate the clock counts for the PWM subsystem
730 ✗ int i = m_pruData->pwmBrightness[0];
731 ✗ while (i == 0) {
732 ✗ i = m_pruData->pwmBrightness[0];
733 }
734 ✗ printf("PERIOD: %X\n", i);
735
736 ✗ int f = i;
737 // f *= 300;
738 // f /= 500;
739 ✗ i = f;
740 ✗ printf("New PERIOD: %X\n", i);
741
742 ✗ i *= m_brightness;
743 ✗ i /= 10;
744 ✗ for (int x = 0; x < 8; x++) {
745 ✗ printf("%d: %X\n", x, i);
746 ✗ m_pruData->pwmBrightness[7 - x] = i;
747 ✗ i /= 2;
748 }
749 }
750 /*
751 for (int x = 0; x < 8; x++) {
752 printf("%d R1: %d %8X\n",x, m_pinInfo[x].row[0].r_gpio, m_pinInfo[x].row[0].r_pin);
753 printf(" G1: %d %8X\n", m_pinInfo[x].row[0].g_gpio, m_pinInfo[x].row[0].g_pin);
754 printf(" B1: %d %8X\n", m_pinInfo[x].row[0].b_gpio, m_pinInfo[x].row[0].b_pin);
755 printf(" R2: %d %8X\n", m_pinInfo[x].row[1].r_gpio, m_pinInfo[x].row[1].r_pin);
756 printf(" G2: %d %8X\n", m_pinInfo[x].row[1].g_gpio, m_pinInfo[x].row[1].g_pin);
757 printf(" B2: %d %8X\n", m_pinInfo[x].row[1].b_gpio, m_pinInfo[x].row[1].b_pin);
758 }
759 */
760
761
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 buildPrepTables();
762
763 // make sure the PRU starts outputting a blank frame to remove any random noise
764 // from the panels
765 14 m_fullFrameLen = gpioFrameLen * 4;
766 // round up to next page boundary
767 14 int alignedLen = m_fullFrameLen + 8192;
768 14 alignedLen -= (alignedLen & 0xFFF);
769
770 // the frame flipping slots come from the shared DDR region allocator so
771 // string/serial outputs on the same region (e.g. the K8-B-Scroller runs
772 // BBB48String + BBBSerial + panels) cannot overlap us; the cape's
773 // dataOffset is kept as a floor for the historic layout. 4 slots is
774 // plenty (3 is the tear-free flip threshold) and leaving the rest of
775 // the region free lets the other outputs grow on a partial reload.
776 14 m_numFrames = 4;
777 14 uint8_t* framesBase = nullptr;
778 14 uint32_t framesPhys = 0;
779
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 while (m_numFrames > 0) {
780
2/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
16 framesBase = BBBPru::ddrAlloc("BBBMatrix", (size_t)alignedLen * m_numFrames, framesPhys, m_dataOffset);
781
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
16 if (framesBase) {
782 14 break;
783 }
784 2 m_numFrames--;
785 }
786
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!framesBase) {
787 ✗ LogErr(VB_CHANNELOUT, "BBBMatrix: no room in the PRU DDR region for even one frame\n");
788 ✗ AddOutputConfigWarning(20, "BBBMatrix: no room in the PRU DDR region - reduce panel count/color depth or other outputs' buffers");
789 ✗ return 0;
790 }
791
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 14 times.
126 for (int x = 0; x < 8; x++) {
792 112 m_frames[x] = framesBase + (size_t)std::min(x, m_numFrames - 1) * alignedLen;
793 }
794 14 m_pruData->address_dma = framesPhys;
795 14 m_curFrame = 0;
796
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (PixelOverlayManager::INSTANCE.isAutoCreatePixelOverlayModels()) {
797
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 std::string dd = "LED Panels";
798
4/16
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 14 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 14 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
14 if (config.isMember("LEDPanelMatrixName") && !config["LEDPanelMatrixName"].asString().empty()) {
799 ✗ dd = config["LEDPanelMatrixName"].asString();
800 }
801
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
14 if (config.isMember("description")) {
802 ✗ dd = config["description"].asString();
803 }
804
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 std::string desc = dd;
805 14 int count = 0;
806
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
14 while (PixelOverlayManager::INSTANCE.getModel(desc) != nullptr) {
807 ✗ count++;
808 ✗ desc = dd + "-" + std::to_string(count);
809 }
810
4/8
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
98 PixelOverlayManager::INSTANCE.addAutoOverlayModel(desc,
811 m_startChannel, m_channelCount, 3,
812
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 "H", m_invertedData ? "BL" : "TL",
813 14 m_height, 1);
814
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 m_autoCreatedModelName = desc;
815 14 }
816 // We need to send the data once to make sure the panels are cleared and "off"
817 // However, this doesn't always work so we'll set everything slightly "on" first
818 // real quick to make sure all the memory changes and is properly mapped into
819 // the process, then we'll reset everything to off
820
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 14 times.
68 for (int x = 0; x < m_numFrames; x++) {
821 54 memset(m_frames[x], 0x1, m_fullFrameLen);
822 }
823
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 msync(m_frames[0], m_pru->ddr_size, MS_SYNC | MS_INVALIDATE);
824
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 SendData(nullptr);
825
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 14 times.
68 for (int x = 0; x < m_numFrames; x++) {
826 54 memset(m_frames[x], 0x0, m_fullFrameLen);
827 }
828
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 msync(m_frames[0], m_pru->ddr_size, MS_SYNC | MS_INVALIDATE);
829
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 SendData(nullptr);
830
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 return ChannelOutput::Init(config);
831 15 }
832
833 14 int BBBMatrix::Close(void) {
834 14 LogDebug(VB_CHANNELOUT, "BBBMatrix::Close()\n");
835
2/4
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
14 BBBPru::ddrRelease("BBBMatrix");
836
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (!m_autoCreatedModelName.empty()) {
837 14 PixelOverlayManager::INSTANCE.removeAutoOverlayModel(m_autoCreatedModelName);
838 }
839
840 // Send the stop command
841 14 m_pruData->command = 0xFF;
842
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (m_pru) {
843 14 m_pru->stop();
844
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 delete m_pru;
845 14 m_pru = nullptr;
846 }
847
848
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 5 times.
14 if (m_pruCopy) {
849 9 m_pruCopy->stop();
850
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 delete m_pruCopy;
851 9 m_pruCopy = nullptr;
852 }
853 14 return ChannelOutput::Close();
854 }
855
856 static int fcount = 0;
857
858 ✗ void BBBMatrix::printStats() {
859 FILE* rfile;
860 ✗ rfile = fopen("/tmp/framerates.txt", "w");
861 ✗ for (int x = 0; x < m_colorDepth; ++x) {
862 ✗ fprintf(rfile, "DV: %d %8X %8X\n", (m_colorDepth - x), brightnessValues[x], delayValues[x]);
863 }
864 ✗ int off = 0;
865 ✗ uint32_t total = 0;
866 ✗ int count = 0;
867 ✗ for (int x = 0; x < m_panelScan; ++x) {
868 ✗ for (int y = m_colorDepth; y > 0; --y) {
869 ✗ fprintf(rfile, "r%2d b%2d: %8X %8X %8X\n", x, y, m_pruData->stats[off], m_pruData->stats[off + 1], m_pruData->stats[off + 2]);
870 ✗ total += m_pruData->stats[off];
871 ✗ count++;
872 ✗ off += 3;
873 }
874 }
875 ✗ fprintf(rfile, "Average Per Row/Bit: %8X\n", (total / count));
876 // printf("0x%X\n", (total / count));
877 ✗ fclose(rfile);
878 ✗ }
879
880 27 void BBBMatrix::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
881 27 addRange(m_startChannel, m_startChannel + m_channelCount - 1);
882 27 }
883
884 ✗ void BBBMatrix::OverlayTestData(unsigned char* channelData, int cycleNum, float percentOfCycle, int testType, const Json::Value& config) {
885 ✗ for (int output = 0; output < m_outputs; output++) {
886 ✗ int panelsOnOutput = m_panelMatrix->m_outputPanels[output].size();
887 ✗ for (int i = 0; i < panelsOnOutput; i++) {
888 ✗ int panel = m_panelMatrix->m_outputPanels[output][i];
889
890 ✗ m_panelMatrix->m_panels[panel].drawTestPattern(channelData + m_startChannel, cycleNum, percentOfCycle, testType);
891 }
892 }
893 ✗ }
894
895 14 void BBBMatrix::buildPrepTables() {
896 // per output: the 4 GPIO bank words for every combination of the 6 pins,
897 // so setting the pins for a pixel/bit is 4 unconditional ORs instead of
898 // 6 data dependent branches
899
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 14 times.
45 for (int o = 0; o < m_outputs; o++) {
900 31 const GPIOPinInfo::Pins& p0 = m_pinInfo[o].row[0];
901 31 const GPIOPinInfo::Pins& p1 = m_pinInfo[o].row[1];
902
2/2
✓ Branch 0 taken 1984 times.
✓ Branch 1 taken 31 times.
2015 for (int idx = 0; idx < 64; idx++) {
903 1984 uint32_t v[4] = { 0, 0, 0, 0 };
904
2/2
✓ Branch 0 taken 992 times.
✓ Branch 1 taken 992 times.
1984 if (idx & 1)
905 992 v[p0.r_gpio] |= p0.r_pin;
906
2/2
✓ Branch 0 taken 992 times.
✓ Branch 1 taken 992 times.
1984 if (idx & 2)
907 992 v[p0.g_gpio] |= p0.g_pin;
908
2/2
✓ Branch 0 taken 992 times.
✓ Branch 1 taken 992 times.
1984 if (idx & 4)
909 992 v[p0.b_gpio] |= p0.b_pin;
910
2/2
✓ Branch 0 taken 992 times.
✓ Branch 1 taken 992 times.
1984 if (idx & 8)
911 992 v[p1.r_gpio] |= p1.r_pin;
912
2/2
✓ Branch 0 taken 992 times.
✓ Branch 1 taken 992 times.
1984 if (idx & 16)
913 992 v[p1.g_gpio] |= p1.g_pin;
914
2/2
✓ Branch 0 taken 992 times.
✓ Branch 1 taken 992 times.
1984 if (idx & 32)
915 992 v[p1.b_gpio] |= p1.b_pin;
916
2/2
✓ Branch 0 taken 7936 times.
✓ Branch 1 taken 1984 times.
9920 for (int g = 0; g < 4; g++) {
917 7936 m_pinLUT[o][idx][g] = v[g];
918 }
919 }
920 }
921 // spread the gamma corrected value so bit b lands in byte b; shifting
922 // six spread values together produces the 6 bit pin combination for
923 // every bit plane of a pixel in a couple of operations
924
2/2
✓ Branch 0 taken 3584 times.
✓ Branch 1 taken 14 times.
3598 for (int v = 0; v < 256; v++) {
925 3584 uint16_t g = gammaCurve[v];
926 3584 uint64_t lo = 0;
927 3584 uint32_t hi = 0;
928
2/2
✓ Branch 0 taken 28672 times.
✓ Branch 1 taken 3584 times.
32256 for (int b = 0; b < 8; b++) {
929
2/2
✓ Branch 0 taken 14335 times.
✓ Branch 1 taken 14337 times.
28672 if ((g >> b) & 1)
930 14335 lo |= 1ULL << (8 * b);
931 }
932
2/2
✓ Branch 0 taken 14336 times.
✓ Branch 1 taken 3584 times.
17920 for (int b = 8; b < 12; b++) {
933
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14336 times.
14336 if ((g >> b) & 1)
934 ✗ hi |= 1u << (8 * (b - 8));
935 }
936 3584 m_gammaSpreadLo[v] = lo;
937 3584 m_gammaSpreadHi[v] = hi;
938 }
939 // group the panels by chain position; all outputs at the same chain
940 // position write into the same gpio words and can accumulate in registers
941
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 m_chainPanels.assign(m_longestChain, {});
942
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 14 times.
45 for (int output = 0; output < m_outputs; output++) {
943
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 31 times.
49 for (int i = 0; i < m_panelMatrix->m_outputPanels[output].size(); i++) {
944 18 int panel = m_panelMatrix->m_outputPanels[output][i];
945 18 int chain = m_panelMatrix->m_panels[panel].chain;
946
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 if (chain >= 0 && chain < m_longestChain) {
947
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 m_chainPanels[chain].emplace_back(output, panel);
948 }
949 }
950 }
951 // the interleave mapping only depends on the coordinates, not the panel
952 // or output, so it can be precomputed once instead of a virtual call per
953 // pixel per frame
954 14 int h2 = m_panelHeight / 2;
955
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 m_yOutMap.resize(h2);
956
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 m_xOutMap.resize(h2 * m_panelWidth);
957
2/2
✓ Branch 0 taken 228 times.
✓ Branch 1 taken 14 times.
242 for (int y = 0; y < h2; y++) {
958 228 int xo = 0;
959 228 int yo = y;
960 228 m_handler->map(xo, yo);
961 228 m_yOutMap[y] = yo;
962
2/2
✓ Branch 0 taken 14912 times.
✓ Branch 1 taken 228 times.
15140 for (int x = 0; x < m_panelWidth; x++) {
963 14912 int xOut = x;
964 14912 int yo2 = y;
965 14912 m_handler->map(xOut, yo2);
966 14912 m_xOutMap[y * m_panelWidth + x] = xOut;
967 }
968 }
969 14 size_t maxPairs = 1;
970
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
42 for (auto& c : m_chainPanels) {
971 28 maxPairs = std::max(maxPairs, c.size());
972 }
973
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 m_stageLo.resize(maxPairs * m_panelWidth * 8);
974
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 m_stageHi.resize(maxPairs * m_panelWidth * 4);
975
976 // PrepData skips the per-frame memset and uses plain stores, which is
977 // only valid if the interleave maps every (y, x) onto a distinct slot of
978 // the chain block (it covers and overwrites everything). If a handler
979 // is not a clean bijection, fall back to memset + OR accumulation which
980 // matches the historical behavior exactly.
981 14 m_prepNeedsMemset = false;
982 14 int blockW = m_panelWidth * m_panelHeight / (m_panelScan * 2);
983
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
28 std::vector<uint8_t> covered(m_panelScan * blockW, 0);
984
2/2
✓ Branch 0 taken 228 times.
✓ Branch 1 taken 14 times.
242 for (int y = 0; y < h2; y++) {
985
2/2
✓ Branch 0 taken 14912 times.
✓ Branch 1 taken 228 times.
15140 for (int x = 0; x < m_panelWidth; x++) {
986 14912 int yOut = m_yOutMap[y];
987 14912 int xOut = m_xOutMap[y * m_panelWidth + x];
988
6/12
✓ Branch 0 taken 14912 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14912 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14912 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14912 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 14912 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 14912 times.
14912 if (yOut < 0 || yOut >= m_panelScan || xOut < 0 || xOut >= blockW || covered[yOut * blockW + xOut]) {
989 ✗ m_prepNeedsMemset = true;
990 } else {
991 14912 covered[yOut * blockW + xOut] = 1;
992 }
993 }
994 }
995
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (m_prepNeedsMemset) {
996 ✗ LogWarn(VB_CHANNELOUT, "BBBMatrix: interleave map for %s is not a clean bijection, using slower PrepData path\n",
997 m_panelInterleave.c_str());
998 }
999 14 }
1000
1001 220 void BBBMatrix::PrepData(unsigned char* channelData) {
1002 220 m_matrix->OverlaySubMatrices(channelData);
1003
1004
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 220 times.
220 if (m_printStats) {
1005 ✗ fcount++;
1006 ✗ if (fcount == 20) {
1007 // every 20 frames or so, save stats
1008 ✗ fcount = 0;
1009 ✗ printStats();
1010 }
1011 }
1012
1013 220 channelData += m_startChannel;
1014
1015 // number of uint32_t per row for each bit
1016 220 size_t rowLen = m_panelWidth * m_longestChain * m_panelHeight / (m_panelScan * 2) * 4; // 4 GPIO's
1017 // number of uint32_t per full row (all bits)
1018 220 size_t fullRowLen = rowLen * m_colorDepth;
1019
1020 220 uint32_t* gpioFrame = m_gpioFrame;
1021 220 int h2 = m_panelHeight / 2;
1022
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 220 times.
220 size_t bitStride = m_outputByRow ? rowLen : (rowLen * m_panelScan);
1023
1024 // For each chain position, the packed bit-plane pin indexes for every
1025 // output are staged for the row, then each bit plane is written with all
1026 // outputs accumulated in a register and stored without a read-modify-
1027 // write. Iterating every chain position and row covers (and thus
1028 // clears) the whole frame, so no memset is needed; bit planes past the
1029 // color depth were zeroed at init and are never touched.
1030 220 const bool rmw = m_prepNeedsMemset;
1031
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 220 times.
220 if (rmw) {
1032 ✗ memset(gpioFrame, 0, m_fullFrameLen);
1033 }
1034
2/2
✓ Branch 0 taken 269 times.
✓ Branch 1 taken 220 times.
489 for (int chain = 0; chain < m_longestChain; chain++) {
1035 269 auto& pairs = m_chainPanels[chain];
1036 269 int nPairs = (int)pairs.size();
1037 269 size_t chainOff = (size_t)(m_longestChain - chain - 1) * 4 * m_panelWidth * m_panelHeight / m_panelScan / 2;
1038
2/2
✓ Branch 0 taken 4328 times.
✓ Branch 1 taken 269 times.
4597 for (int y = 0; y < h2; y++) {
1039 4328 int yw1 = y * m_panelWidth * 3;
1040 4328 int yw2 = (y + h2) * m_panelWidth * 3;
1041 4328 const uint16_t* xMap = &m_xOutMap[y * m_panelWidth];
1042
2/2
✓ Branch 0 taken 3752 times.
✓ Branch 1 taken 4328 times.
8080 for (int p = 0; p < nPairs; p++) {
1043 3752 const int* pixelMap = &m_panelMatrix->m_panels[pairs[p].second].pixelMap[0];
1044 3752 uint8_t* stLo = &m_stageLo[p * m_panelWidth * 8];
1045
2/2
✓ Branch 0 taken 242048 times.
✓ Branch 1 taken 3752 times.
245800 for (int x = 0; x < m_panelWidth; ++x) {
1046 242048 uint64_t pk = m_gammaSpreadLo[channelData[pixelMap[yw1 + x * 3]]] |
1047 242048 (m_gammaSpreadLo[channelData[pixelMap[yw1 + x * 3 + 1]]] << 1) |
1048 242048 (m_gammaSpreadLo[channelData[pixelMap[yw1 + x * 3 + 2]]] << 2) |
1049 242048 (m_gammaSpreadLo[channelData[pixelMap[yw2 + x * 3]]] << 3) |
1050 242048 (m_gammaSpreadLo[channelData[pixelMap[yw2 + x * 3 + 1]]] << 4) |
1051 242048 (m_gammaSpreadLo[channelData[pixelMap[yw2 + x * 3 + 2]]] << 5);
1052 242048 memcpy(&stLo[x * 8], &pk, 8);
1053 }
1054
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3752 times.
3752 if (m_colorDepth > 8) {
1055 ✗ uint8_t* stHi = &m_stageHi[p * m_panelWidth * 4];
1056 ✗ for (int x = 0; x < m_panelWidth; ++x) {
1057 ✗ uint32_t pk = m_gammaSpreadHi[channelData[pixelMap[yw1 + x * 3]]] |
1058 ✗ (m_gammaSpreadHi[channelData[pixelMap[yw1 + x * 3 + 1]]] << 1) |
1059 ✗ (m_gammaSpreadHi[channelData[pixelMap[yw1 + x * 3 + 2]]] << 2) |
1060 ✗ (m_gammaSpreadHi[channelData[pixelMap[yw2 + x * 3]]] << 3) |
1061 ✗ (m_gammaSpreadHi[channelData[pixelMap[yw2 + x * 3 + 1]]] << 4) |
1062 ✗ (m_gammaSpreadHi[channelData[pixelMap[yw2 + x * 3 + 2]]] << 5);
1063 ✗ memcpy(&stHi[x * 4], &pk, 4);
1064 }
1065 }
1066 }
1067
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4328 times.
4328 size_t offset = (m_outputByRow ? (size_t)m_yOutMap[y] * fullRowLen : (size_t)m_yOutMap[y] * rowLen) + chainOff;
1068 // Depths below 8 take the TOP bits of the 8 bit gamma value: the
1069 // bit order is relative to the depth (0..depth-1, which the
1070 // brightness flags index by), so shift it up past the bits the
1071 // depth drops. Without this 6 and 7 bit showed the low bits and
1072 // wrapped - at 6 bit, 136 came out black.
1073
2/2
✓ Branch 0 taken 2736 times.
✓ Branch 1 taken 1592 times.
4328 const int bitShift = m_colorDepth < 8 ? 8 - m_colorDepth : 0;
1074
2/2
✓ Branch 0 taken 29152 times.
✓ Branch 1 taken 4328 times.
33480 for (int bi = 0; bi < m_colorDepth; bi++) {
1075 29152 int b = m_bitOrder[bi] + bitShift;
1076 29152 uint32_t* out = &gpioFrame[offset + (size_t)bi * bitStride];
1077
1/2
✓ Branch 0 taken 29152 times.
✗ Branch 1 not taken.
29152 const uint8_t* st = (b < 8) ? m_stageLo.data() : m_stageHi.data();
1078
1/2
✓ Branch 0 taken 29152 times.
✗ Branch 1 not taken.
29152 size_t stStride = (b < 8) ? (m_panelWidth * 8) : (m_panelWidth * 4);
1079
1/2
✓ Branch 0 taken 29152 times.
✗ Branch 1 not taken.
29152 int stMult = (b < 8) ? 8 : 4;
1080
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29152 times.
29152 int bb = (b < 8) ? b : (b - 8);
1081 #ifdef __ARM_NEON
1082
2/2
✓ Branch 0 taken 1881088 times.
✓ Branch 1 taken 29152 times.
1910240 for (int x = 0; x < m_panelWidth; ++x) {
1083 1881088 uint32_t* o2 = &out[(size_t)xMap[x] * 4];
1084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1881088 times.
3762176 uint32x4_t acc = rmw ? vld1q_u32(o2) : vdupq_n_u32(0);
1085
2/2
✓ Branch 0 taken 1586176 times.
✓ Branch 1 taken 1881088 times.
3467264 for (int p = 0; p < nPairs; p++) {
1086 4758528 acc = vorrq_u32(acc, vld1q_u32(m_pinLUT[pairs[p].first][st[p * stStride + x * stMult + bb]]));
1087 }
1088 vst1q_u32(o2, acc);
1089 }
1090 #else
1091 for (int x = 0; x < m_panelWidth; ++x) {
1092 uint32_t* o2 = &out[(size_t)xMap[x] * 4];
1093 uint32_t a0 = 0, a1 = 0, a2 = 0, a3 = 0;
1094 if (rmw) {
1095 a0 = o2[0];
1096 a1 = o2[1];
1097 a2 = o2[2];
1098 a3 = o2[3];
1099 }
1100 for (int p = 0; p < nPairs; p++) {
1101 const uint32_t* t = m_pinLUT[pairs[p].first][st[p * stStride + x * stMult + bb]];
1102 a0 |= t[0];
1103 a1 |= t[1];
1104 a2 |= t[2];
1105 a3 |= t[3];
1106 }
1107 o2[0] = a0;
1108 o2[1] = a1;
1109 o2[2] = a2;
1110 o2[3] = a3;
1111 }
1112 #endif
1113 }
1114 }
1115 }
1116
1117
2/4
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 220 times.
✗ Branch 3 not taken.
220 if ((m_numFrames >= 3) && (m_frames[m_curFrame] != (uint8_t*)gpioFrame)) {
1118 220 memcpy(m_frames[m_curFrame], m_gpioFrame, m_fullFrameLen);
1119 }
1120 220 }
1121 230 int BBBMatrix::SendData(unsigned char* channelData) {
1122 230 LogExcess(VB_CHANNELOUT, "BBBMatrix::SendData(%p)\n", channelData);
1123
1124 // long long startTime = GetTime();
1125 230 uint32_t addr = m_pru->ddr_addr + (uint32_t)(m_frames[m_curFrame] - m_pru->ddr);
1126 230 uint8_t* ptr = m_frames[m_curFrame];
1127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 230 times.
230 if (m_numFrames < 3) {
1128 // if we have less than 3 blocks, we cannot copy
1129 // in prep or we'd get potential tearing/flickering
1130 ✗ memcpy(ptr, m_gpioFrame, m_fullFrameLen);
1131 }
1132 // long long cpyTime = GetTime();
1133
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 165 times.
230 if (m_curFrame == 0) {
1134 65 m_curFrame = m_numFrames - 1;
1135 } else {
1136 165 m_curFrame--;
1137 }
1138
1139 // make sure memory is flushed before command is set to 1
1140 230 msync(ptr, m_fullFrameLen, MS_SYNC | MS_INVALIDATE);
1141 230 __builtin___clear_cache(ptr, ptr + m_fullFrameLen);
1142 230 m_pruData->address_dma = addr;
1143
1144 230 __asm__ __volatile__("" ::
1145 : "memory");
1146 // long long flshTime = GetTime();
1147 230 m_pruData->command = 1;
1148
1149 /*
1150 if (fcount == 0) {
1151 printf("%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x \n",
1152 m_outputFrame[0], m_outputFrame[1], m_outputFrame[2],
1153 m_outputFrame[3], m_outputFrame[4], m_outputFrame[5],
1154 m_outputFrame[6], m_outputFrame[7], m_outputFrame[8],
1155 m_outputFrame[9], m_outputFrame[10], m_outputFrame[11],
1156 m_outputFrame[12], m_outputFrame[13], m_outputFrame[15]
1157 );
1158
1159 uint32_t *t = (uint32_t *)m_pruCopy->data_ram;
1160 printf(" %d %X %X %X %X %X\n", t[0], t[1], t[2], t[3], t[4], t[5]);
1161 }
1162 */
1163 /*
1164 long long endTime = GetTime();
1165 if ((endTime - startTime) > 100) {
1166 printf("cpy: %d flush: %d cmd: %d Total: %d\n", (int)(cpyTime - startTime), (int)(flshTime - cpyTime), (int)(endTime - flshTime), (int)(endTime - startTime));
1167 }
1168 */
1169 230 return m_channelCount;
1170 }
1171
1172 14 void BBBMatrix::DumpConfig(void) {
1173 14 LogDebug(VB_CHANNELOUT, "BBBMatrix::DumpConfig()\n");
1174 14 LogDebug(VB_CHANNELOUT, " Width : %d\n", m_width);
1175 14 LogDebug(VB_CHANNELOUT, " Height : %d\n", m_height);
1176 14 LogDebug(VB_CHANNELOUT, " Rows : %d\n", m_rows);
1177 14 LogDebug(VB_CHANNELOUT, " Row Size : %d\n", m_rowSize);
1178 14 LogDebug(VB_CHANNELOUT, " Color Depth : %d\n", m_colorDepth);
1179 14 LogDebug(VB_CHANNELOUT, " Outputs : %d\n", m_outputs);
1180 14 LogDebug(VB_CHANNELOUT, " Longest Chain : %d\n", m_longestChain);
1181 14 LogDebug(VB_CHANNELOUT, " Inverted Data : %d\n", m_invertedData);
1182
1183 14 ChannelOutput::DumpConfig();
1184 14 }
1185