Skip to content

Commit 194f6bc

Browse files
committed
Fix for bug 6691452 : DO NOT MERGE
As it so happens, there seem to be panels out there who disapprove of sudden changes in their HDMI clock rate. In particular, Sony LCD panels made from around 2010-2011 (including the Sony GTV panel) seem to dislike this behavior. When exposed to a large jump in the clock rate (say from -100pmm to +100ppm in about 30mSec), they seem to panic, blank their audio and video, and then resync. The whole panic process takes about 2 seconds. The HDMI spec says that its clock jitter requirements are defined by their differential signalling eye diagram requirements relative to an "Ideal Recovery Clock" (see section 4.2.3.1 of the HDMI 1.3a spec). Basically, if you pass the eye diagram tests, you pass the clock jitter requirements. We have determined in lab that even being extremely aggressive in our VCXO rate changes does not come even close to violating the HDMI eye diagrams. Its just this era of Sony panels which seem to be upset by this behavior. One way or the other, experiments which the GTV devices have seemed to indicate that a full range sweep of the VCXO done in 10mSec steps over anything faster than 190mSec can cause trouble. Adding a healthy degree of margin to this finding, the fix is to limit the rate of VCXO control change such that it never goes at a rate faster than FullRange/300mSec. Change flagged as do not merge due to the code structure changes to master. This will need to be merged by hand. Signed-off-by: John Grossman <johngro@google.com> Change-Id: Ibfd361fe1cc2cbd4909489e3317fb12e005c6a75
1 parent 0279246 commit 194f6bc

File tree

7 files changed

+251
-58
lines changed

7 files changed

+251
-58
lines changed

services/common_time/Android.mk

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ LOCAL_SRC_FILES := \
1414
common_time_server_packets.cpp \
1515
clock_recovery.cpp \
1616
common_clock.cpp \
17-
main.cpp
17+
main.cpp \
18+
utils.cpp
1819

1920
# Uncomment to enable vesbose logging and debug service.
2021
#TIME_SERVICE_DEBUG=true

services/common_time/clock_recovery.cpp

Lines changed: 112 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ ClockRecoveryLoop::ClockRecoveryLoop(LocalClock* local_clock,
5353

5454
local_clock_can_slew_ = local_clock_->initCheck() &&
5555
(local_clock_->setLocalSlew(0) == OK);
56+
tgt_correction_ = 0;
57+
cur_correction_ = 0;
58+
59+
// Precompute the max rate at which we are allowed to change the VCXO
60+
// control.
61+
uint64_t N = 0x10000ull * 1000ull;
62+
uint64_t D = local_clock_->getLocalFreq() * kMinFullRangeSlewChange_mSec;
63+
LinearTransform::reduce(&N, &D);
64+
while ((N > INT32_MAX) || (D > UINT32_MAX)) {
65+
N >>= 1;
66+
D >>= 1;
67+
LinearTransform::reduce(&N, &D);
68+
}
69+
time_to_cur_slew_.a_to_b_numer = static_cast<int32_t>(N);
70+
time_to_cur_slew_.a_to_b_denom = static_cast<uint32_t>(D);
5671

5772
reset(true, true);
5873

@@ -85,6 +100,9 @@ const int64_t ClockRecoveryLoop::panic_thresh_ = 50000;
85100
const int64_t ClockRecoveryLoop::control_thresh_ = 10000;
86101
const float ClockRecoveryLoop::COmin = -100.0f;
87102
const float ClockRecoveryLoop::COmax = 100.0f;
103+
const uint32_t ClockRecoveryLoop::kMinFullRangeSlewChange_mSec = 300;
104+
const int ClockRecoveryLoop::kSlewChangeStepPeriod_mSec = 10;
105+
88106

89107
void ClockRecoveryLoop::reset(bool position, bool frequency) {
90108
Mutex::Autolock lock(&lock_);
@@ -144,7 +162,7 @@ bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time,
144162
int64_t observed_common;
145163
int64_t delta;
146164
float delta_f, dCO;
147-
int32_t correction_cur;
165+
int32_t tgt_correction;
148166

149167
if (OK != common_clock_->localToCommon(local_time, &observed_common)) {
150168
// Since we just checked to make certain that this conversion was valid,
@@ -254,23 +272,20 @@ bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time,
254272

255273
// Convert PPM to 16-bit int range. Add some guard band (-0.01) so we
256274
// don't get fp weirdness.
257-
correction_cur = CO * 327.66;
275+
tgt_correction = CO * 327.66;
258276

259277
// If there was a change in the amt of correction to use, update the
260278
// system.
261-
if (correction_cur_ != correction_cur) {
262-
correction_cur_ = correction_cur;
263-
applySlew();
264-
}
279+
setTargetCorrection_l(tgt_correction);
265280

266-
LOG_TS("clock_loop %lld %f %f %f %d\n", raw_delta, delta_f, CO, CObias, correction_cur);
281+
LOG_TS("clock_loop %lld %f %f %f %d\n", raw_delta, delta_f, CO, CObias, tgt_correction);
267282

268283
#ifdef TIME_SERVICE_DEBUG
269284
diag_thread_->pushDisciplineEvent(
270285
local_time,
271286
observed_common,
272287
nominal_common_time,
273-
correction_cur,
288+
tgt_correction,
274289
rtt);
275290
#endif
276291

@@ -298,24 +313,109 @@ void ClockRecoveryLoop::reset_l(bool position, bool frequency) {
298313
last_delta_valid_ = false;
299314
last_delta_ = 0;
300315
last_delta_f_ = 0.0;
301-
correction_cur_ = 0x0;
302316
CO = 0.0f;
303317
lastCObias = CObias = 0.0f;
304-
applySlew();
318+
setTargetCorrection_l(0);
319+
applySlew_l();
305320
}
306321

307322
filter_wr_ = 0;
308323
filter_full_ = false;
309324
}
310325

311-
void ClockRecoveryLoop::applySlew() {
326+
void ClockRecoveryLoop::setTargetCorrection_l(int32_t tgt) {
327+
// When we make a change to the slew rate, we need to be careful to not
328+
// change it too quickly as it can anger some HDMI sinks out there, notably
329+
// some Sony panels from the 2010-2011 timeframe. From experimenting with
330+
// some of these sinks, it seems like swinging from one end of the range to
331+
// another in less that 190mSec or so can start to cause trouble. Adding in
332+
// a hefty margin, we limit the system to a full range sweep in no less than
333+
// 300mSec.
334+
if (tgt_correction_ != tgt) {
335+
int64_t now = local_clock_->getLocalTime();
336+
status_t res;
337+
338+
tgt_correction_ = tgt;
339+
340+
// Set up the transformation to figure out what the slew should be at
341+
// any given point in time in the future.
342+
time_to_cur_slew_.a_zero = now;
343+
time_to_cur_slew_.b_zero = cur_correction_;
344+
345+
// Make sure the sign of the slope is headed in the proper direction.
346+
bool needs_increase = (cur_correction_ < tgt_correction_);
347+
bool is_increasing = (time_to_cur_slew_.a_to_b_numer > 0);
348+
if (( needs_increase && !is_increasing) ||
349+
(!needs_increase && is_increasing)) {
350+
time_to_cur_slew_.a_to_b_numer = -time_to_cur_slew_.a_to_b_numer;
351+
}
352+
353+
// Finally, figure out when the change will be finished and start the
354+
// slew operation.
355+
time_to_cur_slew_.doReverseTransform(tgt_correction_,
356+
&slew_change_end_time_);
357+
358+
applySlew_l();
359+
}
360+
}
361+
362+
bool ClockRecoveryLoop::applySlew_l() {
363+
bool ret = true;
364+
365+
// If cur == tgt, there is no ongoing sleq rate change and we are already
366+
// finished.
367+
if (cur_correction_ == tgt_correction_)
368+
goto bailout;
369+
312370
if (local_clock_can_slew_) {
313-
local_clock_->setLocalSlew(correction_cur_);
371+
int64_t now = local_clock_->getLocalTime();
372+
int64_t tmp;
373+
374+
if (now >= slew_change_end_time_) {
375+
cur_correction_ = tgt_correction_;
376+
next_slew_change_timeout_.setTimeout(-1);
377+
} else {
378+
time_to_cur_slew_.doForwardTransform(now, &tmp);
379+
380+
if (tmp > INT16_MAX)
381+
cur_correction_ = INT16_MAX;
382+
else if (tmp < INT16_MIN)
383+
cur_correction_ = INT16_MIN;
384+
else
385+
cur_correction_ = static_cast<int16_t>(tmp);
386+
387+
next_slew_change_timeout_.setTimeout(kSlewChangeStepPeriod_mSec);
388+
ret = false;
389+
}
390+
391+
local_clock_->setLocalSlew(cur_correction_);
314392
} else {
393+
// Since we are not actually changing the rate of a HW clock, we don't
394+
// need to worry to much about changing the slew rate so fast that we
395+
// anger any downstream HDMI devices.
396+
cur_correction_ = tgt_correction_;
397+
next_slew_change_timeout_.setTimeout(-1);
398+
315399
// The SW clock recovery implemented by the common clock class expects
316400
// values expressed in PPM. CO is in ppm.
317401
common_clock_->setSlew(local_clock_->getLocalTime(), CO);
318402
}
403+
404+
bailout:
405+
return ret;
406+
}
407+
408+
int ClockRecoveryLoop::applyRateLimitedSlew() {
409+
Mutex::Autolock lock(&lock_);
410+
411+
int ret = next_slew_change_timeout_.msecTillTimeout();
412+
if (!ret) {
413+
if (applySlew_l())
414+
next_slew_change_timeout_.setTimeout(-1);
415+
ret = next_slew_change_timeout_.msecTillTimeout();
416+
}
417+
418+
return ret;
319419
}
320420

321421
} // namespace android

services/common_time/clock_recovery.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "diag_thread.h"
2727
#endif
2828

29+
#include "utils.h"
30+
2931
namespace android {
3032

3133
class CommonClock;
@@ -42,6 +44,11 @@ class ClockRecoveryLoop {
4244
int64_t data_point_rtt);
4345
int32_t getLastErrorEstimate();
4446

47+
// Applies the next step in any ongoing slew change operation. Returns a
48+
// timeout suitable for use with poll/select indicating the number of mSec
49+
// until the next change should be applied.
50+
int applyRateLimitedSlew();
51+
4552
private:
4653

4754
// Tuned using the "Good Gain" method.
@@ -87,7 +94,8 @@ class ClockRecoveryLoop {
8794
static uint32_t findMinRTTNdx(DisciplineDataPoint* data, uint32_t count);
8895

8996
void reset_l(bool position, bool frequency);
90-
void applySlew();
97+
void setTargetCorrection_l(int32_t tgt);
98+
bool applySlew_l();
9199

92100
// The local clock HW abstraction we use as the basis for common time.
93101
LocalClock* local_clock_;
@@ -104,7 +112,11 @@ class ClockRecoveryLoop {
104112
int32_t last_delta_;
105113
float last_delta_f_;
106114
int32_t integrated_error_;
107-
int32_t correction_cur_;
115+
int32_t tgt_correction_;
116+
int32_t cur_correction_;
117+
LinearTransform time_to_cur_slew_;
118+
int64_t slew_change_end_time_;
119+
Timeout next_slew_change_timeout_;
108120

109121
// Contoller Output.
110122
float CO;
@@ -128,6 +140,15 @@ class ClockRecoveryLoop {
128140
DisciplineDataPoint startup_filter_data_[kStartupFilterSize];
129141
uint32_t startup_filter_wr_;
130142

143+
// Minimum number of milliseconds over which we allow a full range change
144+
// (from rail to rail) of the VCXO control signal. This is the rate
145+
// limiting factor which keeps us from changing the clock rate so fast that
146+
// we get in trouble with certain HDMI sinks.
147+
static const uint32_t kMinFullRangeSlewChange_mSec;
148+
149+
// How much time (in msec) to wait
150+
static const int kSlewChangeStepPeriod_mSec;
151+
131152
#ifdef TIME_SERVICE_DEBUG
132153
sp<DiagThread> diag_thread_;
133154
#endif

services/common_time/common_time_server.cpp

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,11 @@ bool CommonTimeServer::runStateMachine_l() {
202202
// run the state machine
203203
while (!exitPending()) {
204204
struct pollfd pfds[2];
205-
int rc;
205+
int rc, timeout;
206206
int eventCnt = 0;
207207
int64_t wakeupTime;
208+
uint32_t t1, t2;
209+
bool needHandleTimeout = false;
208210

209211
// We are always interested in our wakeup FD.
210212
pfds[eventCnt].fd = mWakeupThreadFD;
@@ -221,10 +223,14 @@ bool CommonTimeServer::runStateMachine_l() {
221223
eventCnt++;
222224
}
223225

226+
t1 = static_cast<uint32_t>(mCurTimeout.msecTillTimeout());
227+
t2 = static_cast<uint32_t>(mClockRecovery.applyRateLimitedSlew());
228+
timeout = static_cast<int>(t1 < t2 ? t1 : t2);
229+
224230
// Note, we were holding mLock when this function was called. We
225231
// release it only while we are blocking and hold it at all other times.
226232
mLock.unlock();
227-
rc = poll(pfds, eventCnt, mCurTimeout.msecTillTimeout());
233+
rc = poll(pfds, eventCnt, timeout);
228234
wakeupTime = mLocalClock.getLocalTime();
229235
mLock.lock();
230236

@@ -238,8 +244,11 @@ bool CommonTimeServer::runStateMachine_l() {
238244
return false;
239245
}
240246

241-
if (rc == 0)
242-
mCurTimeout.setTimeout(kInfiniteTimeout);
247+
if (rc == 0) {
248+
needHandleTimeout = !mCurTimeout.msecTillTimeout();
249+
if (needHandleTimeout)
250+
mCurTimeout.setTimeout(kInfiniteTimeout);
251+
}
243252

244253
// Were we woken up on purpose? If so, clear the eventfd with a read.
245254
if (pfds[0].revents)
@@ -336,9 +345,8 @@ bool CommonTimeServer::runStateMachine_l() {
336345
continue;
337346
}
338347

339-
// Did we wakeup with no signalled events across all of our FDs? If so,
340-
// we must have hit our timeout.
341-
if (rc == 0) {
348+
// Time to handle the timeouts?
349+
if (needHandleTimeout) {
342350
if (!handleTimeout())
343351
LOGE("handleTimeout failed");
344352
continue;
@@ -1325,29 +1333,6 @@ bool CommonTimeServer::sockaddrMatch(const sockaddr_storage& a1,
13251333
}
13261334
}
13271335

1328-
void CommonTimeServer::TimeoutHelper::setTimeout(int msec) {
1329-
mTimeoutValid = (msec >= 0);
1330-
if (mTimeoutValid)
1331-
mEndTime = systemTime() +
1332-
(static_cast<nsecs_t>(msec) * 1000000);
1333-
}
1334-
1335-
int CommonTimeServer::TimeoutHelper::msecTillTimeout() {
1336-
if (!mTimeoutValid)
1337-
return kInfiniteTimeout;
1338-
1339-
nsecs_t now = systemTime();
1340-
if (now >= mEndTime)
1341-
return 0;
1342-
1343-
uint64_t deltaMsec = (((mEndTime - now) + 999999) / 1000000);
1344-
1345-
if (deltaMsec > static_cast<uint64_t>(std::numeric_limits<int>::max()))
1346-
return std::numeric_limits<int>::max();
1347-
1348-
return static_cast<int>(deltaMsec);
1349-
}
1350-
13511336
bool CommonTimeServer::shouldPanicNotGettingGoodData() {
13521337
if (mClient_FirstSyncTX) {
13531338
int64_t now = mLocalClock.getLocalTime();

services/common_time/common_time_server.h

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "clock_recovery.h"
2929
#include "common_clock.h"
3030
#include "common_time_server_packets.h"
31+
#include "utils.h"
3132

3233
#define RTT_LOG_SIZE 30
3334

@@ -104,18 +105,6 @@ class CommonTimeServer : public Thread {
104105
int64_t rxTimes[RTT_LOG_SIZE];
105106
};
106107

107-
class TimeoutHelper {
108-
public:
109-
TimeoutHelper() : mTimeoutValid(false) { }
110-
111-
void setTimeout(int msec);
112-
int msecTillTimeout();
113-
114-
private:
115-
bool mTimeoutValid;
116-
nsecs_t mEndTime;
117-
};
118-
119108
bool threadLoop();
120109

121110
bool runStateMachine_l();
@@ -194,7 +183,7 @@ class CommonTimeServer : public Thread {
194183
bool shouldPanicNotGettingGoodData();
195184

196185
// Helper to keep track of the state machine's current timeout
197-
TimeoutHelper mCurTimeout;
186+
Timeout mCurTimeout;
198187

199188
// common clock, local clock abstraction, and clock recovery loop
200189
CommonClock mCommonClock;

0 commit comments

Comments
 (0)