354 lines
45 KiB
HTML
354 lines
45 KiB
HTML
|
|
<!doctype html>
|
|||
|
|
<html lang="en">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="utf-8">
|
|||
|
|
<title>MIPI DSI Flicker — Root Cause Investigation</title>
|
|||
|
|
<style>
|
|||
|
|
:root {
|
|||
|
|
--arrive-purple: #5f016f;
|
|||
|
|
--arrive-purple-dark: #3e0049;
|
|||
|
|
--arrive-pink: #ff32a2;
|
|||
|
|
--arrive-tint: #faf3fb;
|
|||
|
|
--arrive-pink-tint: #fff0f8;
|
|||
|
|
--text: #1a1a1a;
|
|||
|
|
--muted: #666;
|
|||
|
|
--rule: #e6dbe9;
|
|||
|
|
--pass: #1a7f37;
|
|||
|
|
--fail: #c62a3d;
|
|||
|
|
--warn: #b58105;
|
|||
|
|
}
|
|||
|
|
body { font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI",
|
|||
|
|
Roboto, sans-serif; max-width: 920px; margin: 0 auto;
|
|||
|
|
padding: 0; color: var(--text); background: #fff; }
|
|||
|
|
.page { padding: 0 28px 36px; }
|
|||
|
|
.banner { background: var(--arrive-purple); padding: 18px 28px;
|
|||
|
|
display: flex; align-items: center; gap: 24px;
|
|||
|
|
border-bottom: 4px solid var(--arrive-pink); }
|
|||
|
|
.banner img { height: 64px; width: auto; display: block;
|
|||
|
|
border-radius: 4px; }
|
|||
|
|
.banner .who { color: #fff; font-size: 13px; opacity: 0.9;
|
|||
|
|
line-height: 1.4; }
|
|||
|
|
.banner .who strong { display: block; font-size: 15px; opacity: 1;
|
|||
|
|
margin-bottom: 2px; }
|
|||
|
|
h1 { font-size: 26px; color: var(--arrive-purple);
|
|||
|
|
border-bottom: 3px solid var(--arrive-purple); padding-bottom: 8px;
|
|||
|
|
margin: 32px 0 4px; }
|
|||
|
|
h2 { font-size: 19px; color: var(--arrive-purple); margin: 28px 0 8px;
|
|||
|
|
border-bottom: 1px solid var(--rule); padding-bottom: 4px; }
|
|||
|
|
h3 { font-size: 16px; margin: 18px 0 6px; color: var(--arrive-purple-dark); }
|
|||
|
|
.meta { color: var(--muted); font-size: 13px; margin-bottom: 18px; }
|
|||
|
|
.tldr { background: var(--arrive-tint); border-left: 4px solid var(--arrive-purple);
|
|||
|
|
padding: 14px 18px; margin: 16px 0 24px;
|
|||
|
|
border-radius: 0 6px 6px 0; }
|
|||
|
|
.tldr strong { color: var(--arrive-purple); }
|
|||
|
|
table { border-collapse: collapse; margin: 8px 0 16px; font-size: 14px;
|
|||
|
|
width: 100%; }
|
|||
|
|
th, td { padding: 7px 12px; text-align: left;
|
|||
|
|
border-bottom: 1px solid var(--rule); }
|
|||
|
|
th { background: var(--arrive-purple); color: #fff; font-weight: 600; }
|
|||
|
|
td.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|||
|
|
tbody tr:nth-child(even) { background: var(--arrive-tint); }
|
|||
|
|
.pass { color: var(--pass); font-weight: 600; }
|
|||
|
|
.fail { color: var(--fail); font-weight: 600; }
|
|||
|
|
.warn { color: var(--warn); font-weight: 600; }
|
|||
|
|
code { font: 13px/1.4 "SF Mono", Menlo, Consolas, monospace;
|
|||
|
|
background: var(--arrive-tint); padding: 1px 5px; border-radius: 3px;
|
|||
|
|
color: var(--arrive-purple-dark); }
|
|||
|
|
pre { background: var(--arrive-purple-dark); color: #f7e9f9; padding: 12px 16px;
|
|||
|
|
border-radius: 4px; font: 13px/1.5 "SF Mono", Menlo, Consolas, monospace;
|
|||
|
|
overflow-x: auto; }
|
|||
|
|
pre.diagram { background: var(--arrive-tint); color: var(--text);
|
|||
|
|
border-left: 4px solid var(--arrive-purple); }
|
|||
|
|
.verdict { font-size: 18px; margin: 18px 0; padding: 16px 20px;
|
|||
|
|
background: var(--arrive-pink-tint);
|
|||
|
|
border: 2px solid var(--arrive-pink);
|
|||
|
|
border-radius: 8px; }
|
|||
|
|
.verdict strong.big { font-size: 22px; color: var(--arrive-purple); }
|
|||
|
|
ul.tight li { margin-bottom: 4px; }
|
|||
|
|
.footnote { color: var(--muted); font-size: 12px; margin-top: 32px;
|
|||
|
|
border-top: 1px solid var(--rule); padding-top: 10px; }
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
|
|||
|
|
<div class="banner">
|
|||
|
|
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABq0AAALLCAYAAABjKMhuAAALLmVYSWZJSSoACAAAAAAADgAAAAkA/gAEAAEAAAABAAAAAAEEAAEAAAAAAQAAAQEEAAEAAABrAAAAAgEDAAMAAACAAAAAAwEDAAEAAAAGAAAABgEDAAEAAAAGAAAAFQEDAAEAAAADAAAAAQIEAAEAAACGAAAAAgIEAAEAAACoCgAAAAAAAAgACAAIAP/Y/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAawEAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8ASiiiveP1EKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKRmCKWYgKBkk9qAFoqpp961/A0/kmOMsRGScl1Hf2q3QTGSkrrYKKKKCgooooAKKKKACiiigAooooAKKKytd1QaZYnYR58nyxj09T+FBFScacXKWyIb7xNaWV29v5ckrJwxTGAfSrelatFqscjxRugQgHdivOySxJJJJ5JPeuu8G/8et1/vj+VU1ZHl4XG1Ktfle2p01FFMkljiXdI6ovqxwKk9e9h9FNR0kQPGysp6MpyDTqACuan8XxR3ZSO2LwqcF92CfcDFdB9ogMvk+dH5h/g3DP5VyU/hG7+1sIZIjAW4ZjyB7imrdTixc6yS9h8zroJkuII5ozlHUMp9jUlQ2lstnaRW6HKxqFz6+9TUjrjey5twooooKCiioZbq3gOJp4oz6M4FAm0tWTUjMFGWIA9TTIp4ZwTDKkgHUowNNubWC8i8q4j3pnO0k0CburxCS7t4lLSTxKo7lwK5fXvESXELWlkSUbh5MYyPQVP4g0Oyt9Ne6t4/KeMjgE4YE47/WoNI8LieJbi+LKrDKxDg49z/SqVtzzsRPEzl7GKSv+R0GnXtpNYQmCSNVCAbNwBXjoaughhkEEHuKzV8P6UqgCzU/Vif61oxxrFGsaKFRQAoHYVJ3UvaJWnb5DqKKKDUKKKKACiiigAooooAKKr30k0NjNLbqrSopZQ3Q4riJfEmqSsSLgIP7qIAB/Wmlc5cRi4UGlJPU7yaaO3heaVtqIMsfavOdTv5NSvnnfIB4Rf7q9hRcarfXcJhnuXeMnJU45punzwW19FNcRGWNDnaD37VSVjysVi1iGoLRDr7T5LBLfzeJJY95X+7zwK6Pwd/x53X++P5Vl+JL631C4t5rd9y+Xgg8EHJ4Nang7/jzuv98fyoexWGjGOLtDb/gHId6sSSXeoSlm82d/YE4/wqv3r0zT7SKysooYlCgKMkdz3JobsYYTDOu2r2SOR1RSvhrTFYEEFsgisaG5mgSRYZGQSABtpxkV1XjL/j1tf98/yrO8KWkVxqLySqG8lNygjvnrQtjStRbxKpxfZfgQ+HoJk1u1kaKRUJb5ipA+6az77/kIXP8A11b+Zr02vMr7/kIXP/XVv5mhO7KxeHVClGKd9X+ht+IbTOl6ddqvSJY2P4ZH9aPCF35d7Las2BKu5QfUf/Wz+Vbdza/bPDCxAEt9nVlx6gAiuK0+5NnqEFwP4HBP07/pSWqKrfuMRCp0dv8AJnoGq3X2LTLifuqYX6ngfqa88tbdrq7igXgyMFz6ZrqPF92Bb29qrcufMbHoOn8/0rP8KW3m6k9wy/JAhOfRjwP0zQtFcrF/vsTGktl/TLuv629s39n2TFNgCu46jjoK52CzvL5maGGSY/xMBn8zUMsjTTPK33nYsfqa37TxStlaR28Vgu1FxnzOvv0p7bHM6ka9RutKy6GK8d3p043LLbyjoeVNdj4e1ttRjaC4x9ojGc/3x6/WsHVfEC6paeS9mqMCCr78lf0qpokxg1q0Yd5An58f1oeqLo1VQrJU5Xiztr+ylvri3RmUWkbb5Fzy5HQfSr9FFQe8oJNvuFFFFBQUUUUAFFFFABRRRQAUUUUAB5GDXI6zpel6TZswR3nlJEYZz8vvx2FdceB0zXF6npes6lfPcPaEL0RfMX5V7DrTRw45XhpHmfpexjWNnJf3kdvEPmY8nsB3Nbmo+E5YUMllIZgOsbDDfh61seH9IOm2xkmUC5k+932j0rZptmGHy+LpfvFq/wADysggkEYI6g113g7/AI87r/fH8qg17Qbq41IzWVuGR1BfDAfN36n6VoeGtPurC2uEuYvLZ2BUbgc8e1NvQxwuHqUsTZrRX1OH716nH/qk/wB0VwP/AAjmrf8APof+/i/413yAiNQeoApSNstpzg5cya2/U5vxl/x62v8Avn+VVvBv/H1df7g/nWl4m0+6v7eBbWLzCrEkbgMce9QeGdLvLC4na6h8sMoAO4HPPsaOgSpz+uqVnbv8jpK8yvv+Qhc/9dW/ma9NrhLvw/qkl5PIlqSrSMQd68gn60RKzKnOcY8qudlp/wDyDrX/AK5L/IVwGrWv2PVLiEfdD5X6Hkfzr0KzjaKygjcYZY1Uj0IFYHiXR7m+uYZ7SHzG2lX+YDp06n3NCepWNoudBNLVHLXV3JdtGZD/AKuNY1+grsvC1r5GkCUj5pmLc+g4H+feubXw3qpYA2uAT1Lrx+td5DEsEEcKfdRQo+gFDZhl9GftHUqJ/M82v7ZrO+mt2GNjED6dv0rpdLl0G5s4xcRW0c6qA4kAXJ9RWnrOiRaqgYN5dwowr9iPQ1yNxoOp27EG1dwD1j+bP5c09zKVGphajajzRfkbWo3Wg2aqILS3uZCfuoeAPrTdHvrK91KOOHSI4mGW8wNnbjv0+lZFv4f1O4YAWzRg9Wk+XH9a7DR9Hi0qAgNvmf77/wBB7UnY2oKtVqKTiox9EaVFFFSeuFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB/9mCXMRlAAAAAXNSR0IB2cksfwAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+oFCwopK4GHeUgAACAASURBV
|
|||
|
|
<div class="who">
|
|||
|
|
<strong>Hardware Engineering</strong>
|
|||
|
|
Display interface investigation
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="page">
|
|||
|
|
|
|||
|
|
<h1>MIPI DSI Flicker — Root Cause Investigation</h1>
|
|||
|
|
<div class="meta">
|
|||
|
|
Display flicker on i.MX 8M Mini → SN65DSI83 → LVDS panel<br>
|
|||
|
|
Investigation period: May 7 – May 11, 2026 ·
|
|||
|
|
Author: David Rice
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="tldr">
|
|||
|
|
<strong>TL;DR.</strong> The flicker is caused by the
|
|||
|
|
<code>PUT /video stop</code> path on the i.MX, not by signal-integrity
|
|||
|
|
on the MIPI bus, not by the SN65DSI83 itself, and not by the panel.
|
|||
|
|
Each video stop tears down the DSI HS-clock; the SN65's PLL loses lock
|
|||
|
|
for 15–45 ms (1–3 display frames); on the subsequent start, the
|
|||
|
|
PLL must re-acquire and the LVDS output is briefly garbled — the
|
|||
|
|
visible flicker. <strong>Fix:</strong> change "stop" semantics so the
|
|||
|
|
HS-clock keeps running (e.g. GStreamer <code>PAUSED</code> instead of
|
|||
|
|
<code>NULL</code>), or simply avoid stopping video in production.
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<h2>1. The problem</h2>
|
|||
|
|
<p>
|
|||
|
|
An i.MX 8M Mini SoC drives a SN65DSI83 MIPI-DSI → LVDS bridge into an
|
|||
|
|
LCD panel. Under steady-state operation the display sometimes flickers.
|
|||
|
|
Initial hypothesis: a MIPI bit-error or signal-integrity issue on the
|
|||
|
|
CLK or data lanes.
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<h2>2. Investigation summary</h2>
|
|||
|
|
|
|||
|
|
<table>
|
|||
|
|
<thead><tr><th>Phase</th><th>Approach</th><th>Result</th></tr></thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr><td>1 – proto decoder</td>
|
|||
|
|
<td>Decode raw MIPI bursts from differential captures, look for byte-level anomalies</td>
|
|||
|
|
<td><span class="fail">Inconclusive.</span> Capture coverage was ~0.0004% of time
|
|||
|
|
(20 µs windows on a 10 s cycle). Flicker events not captured.</td></tr>
|
|||
|
|
<tr><td>2 – segmented memory</td>
|
|||
|
|
<td>Use scope's segmented-memory mode to capture 100 LP-trigger events per acquisition
|
|||
|
|
(~50× higher coverage), analyse per-segment</td>
|
|||
|
|
<td><span class="warn">Negative result.</span> Flicker and non-flicker captures
|
|||
|
|
statistically indistinguishable across all LP-state metrics.</td></tr>
|
|||
|
|
<tr><td>3 – SN65 register monitor</td>
|
|||
|
|
<td>High-rate HTTP polling of SN65DSI83's status registers
|
|||
|
|
(<code>csr_0a</code>, <code>csr_e5</code>) to catch transient PLL events
|
|||
|
|
the post-event snapshot missed</td>
|
|||
|
|
<td><span class="pass">Smoking gun found.</span> PLL unlocks during flicker
|
|||
|
|
sessions, never during good sessions.</td></tr>
|
|||
|
|
<tr><td>4 – Pulse-width characterisation</td>
|
|||
|
|
<td>100 Hz polling (median 20 ms) to measure actual unlock pulse width</td>
|
|||
|
|
<td>Pulse width <strong>15–45 ms</strong> (1–3 display frames). Too short for
|
|||
|
|
a software-driven clock pause; too short for a brownout-and-restart.
|
|||
|
|
Consistent with a brief clock-lane disturbance.</td></tr>
|
|||
|
|
<tr><td>5 – Cycling vs hold</td>
|
|||
|
|
<td>Compare PLL behaviour under continuous video to behaviour under
|
|||
|
|
on/off cycling</td>
|
|||
|
|
<td><span class="pass">Definitive.</span> 0 unlocks in 10 min 51 s of
|
|||
|
|
continuous video. 30 unlocks in 9 min of cycling. The cycling is
|
|||
|
|
the trigger.</td></tr>
|
|||
|
|
<tr><td>6 – Transition isolation</td>
|
|||
|
|
<td>Time-correlate every unlock against each <code>PUT /video start</code>
|
|||
|
|
and <code>PUT /video stop</code> event</td>
|
|||
|
|
<td><span class="pass">Conclusive.</span> 100% of unlocks happen 225–259 ms
|
|||
|
|
after <code>stop</code>. 0% after <code>start</code>.</td></tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<h2>3. Key data</h2>
|
|||
|
|
|
|||
|
|
<h3>3.1 Continuous video (hold) — baseline</h3>
|
|||
|
|
<table>
|
|||
|
|
<thead><tr><th>Run</th><th class="num">Duration</th><th class="num">PLL unlocks</th>
|
|||
|
|
<th class="num">Rate</th><th class="num">I²C read errors</th></tr></thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr><td>Hold (no cycling)</td><td class="num">650.9 s</td>
|
|||
|
|
<td class="num pass">0</td><td class="num pass">0.0/min</td>
|
|||
|
|
<td class="num pass">0.0%</td></tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<h3>3.2 Video on/off cycling</h3>
|
|||
|
|
<table>
|
|||
|
|
<thead><tr><th>Run</th><th class="num">Duration</th><th class="num">Cycles</th>
|
|||
|
|
<th class="num">PLL unlocks</th><th class="num">Unlocks / cycle</th></tr></thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr><td>May 11 — 30 unlock-recovery pairs</td>
|
|||
|
|
<td class="num">~9 min</td><td class="num">~54</td>
|
|||
|
|
<td class="num fail">30</td><td class="num fail">0.56</td></tr>
|
|||
|
|
<tr><td>May 11 — controlled (17 cycles)</td>
|
|||
|
|
<td class="num">177 s</td><td class="num">17</td>
|
|||
|
|
<td class="num fail">8</td><td class="num fail">0.47</td></tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<h3>3.3 Unlock pulse-width distribution</h3>
|
|||
|
|
<table>
|
|||
|
|
<thead><tr><th>Metric</th><th class="num">Value</th><th>Notes</th></tr></thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr><td>min</td> <td class="num">14.5 ms</td><td>under 1 frame at 60 Hz</td></tr>
|
|||
|
|
<tr><td>median</td> <td class="num">21.3 ms</td><td>~1.3 frames</td></tr>
|
|||
|
|
<tr><td>p90</td> <td class="num">40.0 ms</td><td>~2.4 frames</td></tr>
|
|||
|
|
<tr><td>max</td> <td class="num">44.5 ms</td><td>~2.7 frames</td></tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<div class="verdict">
|
|||
|
|
<strong class="big">Transition-isolation verdict (n = 8)</strong><br><br>
|
|||
|
|
Unlocks <strong>after STOP:</strong> <strong>8 / 8 (100%)</strong> ·
|
|||
|
|
median offset <strong>230 ms</strong> (range 225–259 ms)<br>
|
|||
|
|
Unlocks <strong>after START:</strong> 0 / 8 (0%)<br>
|
|||
|
|
Unlocks unattributable to either: 0 / 8 (0%)
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<h2>4. Mechanism</h2>
|
|||
|
|
|
|||
|
|
<pre class="diagram">
|
|||
|
|
PUT /video stop arrives
|
|||
|
|
│
|
|||
|
|
│ ~5 ms HTTP / Flask processing
|
|||
|
|
│ ~50-150ms App + GStreamer pipeline tears down
|
|||
|
|
│ (state goes to NULL)
|
|||
|
|
▼
|
|||
|
|
DSIM driver disables HS_CLK_EN ──────► ~230 ms after stop
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
MIPI CLK lane goes to LP-11
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
SN65DSI83 sees no reference clock
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
PLL drops lock ◄── csr_e5.pll_unlock = 1 caught here
|
|||
|
|
(pulse width 15-45 ms)
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
PLL re-acquires to "no-signal" idle state
|
|||
|
|
│
|
|||
|
|
(~500 ms OFF window)
|
|||
|
|
│
|
|||
|
|
PUT /video start
|
|||
|
|
▼
|
|||
|
|
DSIM re-enables HS_CLK_EN; MIPI traffic resumes
|
|||
|
|
│
|
|||
|
|
▼
|
|||
|
|
SN65DSI83 PLL has to re-acquire to the new clock
|
|||
|
|
│ (~10-30 ms, LVDS output is garbage during this)
|
|||
|
|
▼
|
|||
|
|
──── visible flicker on the panel ────
|
|||
|
|
</pre>
|
|||
|
|
|
|||
|
|
<p>
|
|||
|
|
The bridge is behaving correctly: a PLL is expected to lose lock when its
|
|||
|
|
reference clock disappears. The defect is upstream — the act of
|
|||
|
|
<em>stopping the video</em> drops the MIPI HS-clock, which puts the bridge
|
|||
|
|
through an unlock-relock cycle every time, and the next start has to
|
|||
|
|
re-acquire from cold. That re-acquisition window is the visible flicker.
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<h2>5. Recommended fix</h2>
|
|||
|
|
|
|||
|
|
<p>Two orthogonal options. Either should eliminate the flicker.</p>
|
|||
|
|
|
|||
|
|
<h3>5.1 Don't tear the pipeline down</h3>
|
|||
|
|
<p>
|
|||
|
|
In the device-side video stack, change the "stop" path from a full
|
|||
|
|
teardown to a soft pause that <strong>keeps the DSI HS-clock running</strong>.
|
|||
|
|
For GStreamer:
|
|||
|
|
</p>
|
|||
|
|
<pre>// Today (causes flicker):
|
|||
|
|
gst_element_set_state(pipeline, GST_STATE_NULL);
|
|||
|
|
|
|||
|
|
// Proposed:
|
|||
|
|
gst_element_set_state(pipeline, GST_STATE_PAUSED);
|
|||
|
|
</pre>
|
|||
|
|
<p>
|
|||
|
|
<code>PAUSED</code> retains the pipeline graph and buffers — and, importantly,
|
|||
|
|
doesn't trigger the bridge-disable path in the i.MX DSIM driver, so HS-CLK
|
|||
|
|
stays on and the SN65 PLL stays locked through the transition. Resume is
|
|||
|
|
near-instant and visually clean.
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<h3>5.2 Don't stop video in production</h3>
|
|||
|
|
<p>
|
|||
|
|
If the only reason <code>/video stop</code> is called in real deployments
|
|||
|
|
is the flicker test harness itself, the flicker mode is purely an artefact
|
|||
|
|
of the test. Production code that starts the stream once at boot and
|
|||
|
|
leaves it running will see <strong>zero</strong> PLL unlocks (confirmed
|
|||
|
|
empirically — 0 unlocks in 10 min 51 s of continuous video).
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<h3>5.3 Verify the fix</h3>
|
|||
|
|
<p>
|
|||
|
|
Once the device server gains a soft-stop action (e.g.
|
|||
|
|
<code>{"action": "pause"}</code>), <code>compare_stops.py</code> in this
|
|||
|
|
repo runs an A/B test automatically:
|
|||
|
|
</p>
|
|||
|
|
<pre>STYLES = [
|
|||
|
|
("stop_full", {"action": "start", "mode": "static-pink"}, {"action": "stop"}),
|
|||
|
|
("stop_pause", {"action": "start", "mode": "static-pink"}, {"action": "pause"}),
|
|||
|
|
]
|
|||
|
|
$ python3 compare_stops.py --cycles 30
|
|||
|
|
</pre>
|
|||
|
|
<p>
|
|||
|
|
A successful fix will show <code>~0.5</code> unlocks/cycle for
|
|||
|
|
<code>stop_full</code> and <code>0.00</code> for <code>stop_pause</code>.
|
|||
|
|
</p>
|
|||
|
|
|
|||
|
|
<h2>6. Tools developed</h2>
|
|||
|
|
<table>
|
|||
|
|
<thead><tr><th>Script</th><th>Purpose</th></tr></thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr><td><code>sn65_monitor.py</code></td>
|
|||
|
|
<td>Polls SN65 status registers at 50–100 Hz, logs every PLL transition with
|
|||
|
|
millisecond timestamps. Rolling 30 s buffer dumped to JSON on
|
|||
|
|
<code>f</code>/<code>g</code> keypress.</td></tr>
|
|||
|
|
<tr><td><code>video_cycler.py</code></td>
|
|||
|
|
<td>Toggles <code>/video</code> start/stop on the device on a configurable
|
|||
|
|
cadence. Logs every transition to a CSV. Has a <code>--hold</code>
|
|||
|
|
mode for the no-cycling baseline.</td></tr>
|
|||
|
|
<tr><td><code>analyze_session.py</code></td>
|
|||
|
|
<td>Cross-references the latest SN65 log with the latest cycle log,
|
|||
|
|
classifies each unlock as "after_START / after_STOP / neither",
|
|||
|
|
prints a verdict.</td></tr>
|
|||
|
|
<tr><td><code>compare_stops.py</code></td>
|
|||
|
|
<td>Runs a controlled A/B/… test across multiple stop-style payloads.
|
|||
|
|
Polls SN65 inline, attributes unlocks to the active style,
|
|||
|
|
prints a comparison table. Use this to verify the eventual fix.</td></tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
|
|||
|
|
<h2>7. Open questions</h2>
|
|||
|
|
<ul class="tight">
|
|||
|
|
<li><strong>The rare catastrophic black-screen mode.</strong> Most unlocks
|
|||
|
|
recover cleanly within ~40 ms. Twice during the investigation a
|
|||
|
|
flicker resulted in a persistent black screen. That is presumably the
|
|||
|
|
same root cause but where PLL re-acquisition fails outright; eliminating
|
|||
|
|
the trigger should eliminate the rare variant too, but worth
|
|||
|
|
confirming with extended runs.</li>
|
|||
|
|
<li><strong>The 8 % of "good" sessions that contained early unlock activity.</strong>
|
|||
|
|
One <code>f</code> press on 11 May (08:33:28) had zero PLL activity in
|
|||
|
|
the preceding minute, suggesting either an observation false-positive
|
|||
|
|
or a separate, sub-poll-rate fault path. Worth keeping the
|
|||
|
|
<code>sn65_monitor</code> running in any future regression testing
|
|||
|
|
to catch.</li>
|
|||
|
|
<li><strong>Device-side endpoint additions.</strong> Three small additions
|
|||
|
|
would close out the diagnostic kit:
|
|||
|
|
<ul>
|
|||
|
|
<li><code>POST /video</code> support for <code>action=pause</code>
|
|||
|
|
(GStreamer <code>GST_STATE_PAUSED</code>)</li>
|
|||
|
|
<li><code>/registers</code> to expose the runtime DSIM registers
|
|||
|
|
<code>DSIM_STATUS</code>, <code>DSIM_CLKCTRL</code>,
|
|||
|
|
<code>DSIM_INTSRC</code>, <code>DSIM_FIFOCTRL</code> alongside the
|
|||
|
|
existing PHY-timing config registers</li>
|
|||
|
|
<li><code>GET /video/state</code> exposing the GStreamer pipeline
|
|||
|
|
state (NULL / READY / PAUSED / PLAYING), which would let us
|
|||
|
|
time-correlate the actual pipeline state transitions with the
|
|||
|
|
PLL unlock window</li>
|
|||
|
|
</ul>
|
|||
|
|
</li>
|
|||
|
|
</ul>
|
|||
|
|
|
|||
|
|
<div class="footnote">
|
|||
|
|
Investigation traces, raw register snapshots, and the analysis scripts
|
|||
|
|
are in this repository under <code>data/sn65_log/</code>,
|
|||
|
|
<code>data/cycle_logs/</code>, and <code>data/compare_logs/</code>.
|
|||
|
|
Each timestamped run is independently reproducible.
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</div> <!-- /.page -->
|
|||
|
|
|
|||
|
|
</body>
|
|||
|
|
</html>
|