blob: 99d0f239e659aeb1c150bda2abea598af78414e2 [file] [log] [blame]
Yalong Liu1df84372018-01-24 17:10:12 +08001#!/usr/bin/env python2
2# Copyright 2015 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import sys
7
8# Things seem to magically change in the tables at these TMDS rates.
9# Specifically looking at NO pixel repetition in the table:
10#
11# 0 - 44.9 - output divider is 0b11
12# 49.5 - 90.0 - output divider is 0b10
13# 94.5 - 182.75 - output divider is 0b01
14# 185.625 - - output divider is 0b00
15#
16# You can also notice that MPLL charge pump settings change at similar times.
17
18RATE1 = 46000000
19RATE2 = 92000000
20RATE3 = 184000000
21
22
23def make_mpll(rate, depth, pixel_rep=0):
24 assert pixel_rep == 0, "Untested with non-zero pixel rep and probably wrong"
25
26 tmds = (rate * depth) / 8. * (pixel_rep + 1)
27
28 if depth == 8:
29 prep_div = 0
30 elif depth == 10:
31 prep_div = 1
32 elif depth == 12:
33 prep_div = 2
34 elif depth == 16:
35 prep_div = 3
36
37 # Rates higher than 340MHz are HDMI 2.0
38 # From tables, tmdsmhl_cntrl is 100% correlated with HDMI 1.4 vs 2.0
39 if tmds <= 340000000:
40 opmode = 0 # HDMI 1.4
41 tmdsmhl_cntrl = 0x0
42 else:
43 opmode = 1 # HDMI 2.0
44 tmdsmhl_cntrl = 0x3
45
46 # Keep the rate within the proper range with the output divider control
47 if tmds <= RATE1:
48 n_cntrl = 0x3 # output divider: 0b11
49 elif tmds <= RATE2:
50 n_cntrl = 0x2 # output divider: 0b10
51 elif tmds <= RATE3:
52 n_cntrl = 0x1 # output divider: 0b01
53 else:
54 n_cntrl = 0x0 # output divider: 0b00
55
56 # Need to make the dividers work out
57 #
58 # This could be done algorithmically, but let's not for now. We show the
59 # math to make this work out below as an assert.
60 if n_cntrl == 0x3:
61 if depth == 8:
62 fbdiv2_cntrl = 0x2 # feedback div1: / 2 (no +1)
63 fbdiv1_cntrl = 0x3 # feedback div2: / 4
64 ref_cntrl = 0x0 # input divider: / 1
65 elif depth == 10:
66 fbdiv2_cntrl = 0x5 # feedback div1: / 5 (no +1)
67 fbdiv1_cntrl = 0x1 # feedback div2: / 2
68 ref_cntrl = 0x0 # input divider: / 1
69 elif depth == 12:
70 fbdiv2_cntrl = 0x3 # feedback div1: / 3 (no +1)
71 fbdiv1_cntrl = 0x3 # feedback div2: / 4
72 ref_cntrl = 0x0 # input divider: / 1
73 elif depth == 16:
74 # Guess:
75 fbdiv2_cntrl = 0x4 # feedback div1: / 4 (no +1)
76 fbdiv1_cntrl = 0x3 # feedback div2: / 4
77 ref_cntrl = 0x0 # input divider: / 1
78
79 elif n_cntrl == 0x2:
80 if depth == 8:
81 fbdiv2_cntrl = 0x1 # feedback div1: / 1 (no +1)
82 fbdiv1_cntrl = 0x3 # feedback div2: / 4
83 ref_cntrl = 0x0 # input divider: / 1
84 elif depth == 10:
85 fbdiv2_cntrl = 0x5 # feedback div1: / 5 (no +1)
86 fbdiv1_cntrl = 0x0 # feedback div2: / 1
87 ref_cntrl = 0x0 # input divider: / 1
88 elif depth == 12:
89 fbdiv2_cntrl = 0x2 # feedback div1: / 2 (no +1)
90 fbdiv1_cntrl = 0x2 # feedback div2: / 3
91 ref_cntrl = 0x0 # input divider: / 1
92 elif depth == 16:
93 fbdiv2_cntrl = 0x2 # feedback div1: / 2 (no +1)
94 fbdiv1_cntrl = 0x3 # feedback div2: / 4
95 ref_cntrl = 0x0 # input divider: / 1
96
97 elif n_cntrl == 0x1:
98 if depth == 8:
99 fbdiv2_cntrl = 0x1 # feedback div1: / 1 (no +1)
100 fbdiv1_cntrl = 0x1 # feedback div2: / 2
101 ref_cntrl = 0x0 # input divider: / 1
102 elif depth == 10:
103 fbdiv2_cntrl = 0x5 # feedback div1: / 5 (no +1)
104 fbdiv1_cntrl = 0x0 # feedback div2: / 1
105 ref_cntrl = 0x1 # input divider: / 2
106 elif depth == 12:
107 fbdiv2_cntrl = 0x1 # feedback div1: / 1 (no +1)
108 fbdiv1_cntrl = 0x2 # feedback div2: / 3
109 ref_cntrl = 0x0 # input divider: / 1
110 elif depth == 16:
111 fbdiv2_cntrl = 0x1 # feedback div1: / 1 (no +1)
112 fbdiv1_cntrl = 0x3 # feedback div2: / 4
113 ref_cntrl = 0x0 # input divider: / 1
114
115 elif n_cntrl == 0x0:
116 if depth == 8:
117 fbdiv2_cntrl = 0x1 # feedback div1: / 1 (no +1)
118 fbdiv1_cntrl = 0x0 # feedback div2: / 1
119 ref_cntrl = 0x0 # input divider: / 1
120 elif depth == 10:
121 fbdiv2_cntrl = 0x5 # feedback div1: / 5 (no +1)
122 fbdiv1_cntrl = 0x0 # feedback div2: / 1
123 ref_cntrl = 0x3 # input divider: / 4
124 elif depth == 12:
125 fbdiv2_cntrl = 0x1 # feedback div1: / 1 (no +1)
126 fbdiv1_cntrl = 0x2 # feedback div2: / 3
127 ref_cntrl = 0x1 # input divider: / 2
128 elif depth == 16:
129 fbdiv2_cntrl = 0x1 # feedback div1: / 1 (no +1)
130 fbdiv1_cntrl = 0x1 # feedback div2: / 2
131 ref_cntrl = 0x0 # input divider: / 1
132
133 # Double check with math; this formula derived from the table.
134 total_div = (fbdiv2_cntrl * (fbdiv1_cntrl + 1) * (1 << (3 - n_cntrl)) /
135 (ref_cntrl + 1))
136 assert depth == total_div, \
137 "Error with rate=%d, tmds=%d, depth=%d, n_cntrl=%d, pixel_rep=%d" % (
138 rate, tmds, depth, n_cntrl, pixel_rep)
139
140 # Could be done by math, but this makes it more obvious I think...
141 if n_cntrl == 3:
142 gmp_cntrl = 0
143 elif n_cntrl == 2:
144 gmp_cntrl = 1
145 elif n_cntrl == 1:
146 gmp_cntrl = 2
147 elif n_cntrl == 0:
148 gmp_cntrl = 3
149
150 return ((n_cntrl << 0) |
151 (ref_cntrl << 2) |
152 (fbdiv1_cntrl << 4) |
153 (fbdiv2_cntrl << 6) |
154 (opmode << 9) |
155 (tmdsmhl_cntrl << 11) |
156 (prep_div << 13),
157 gmp_cntrl)
158
159def do_mpll_loop():
160 mpll_cfg_table = {}
161 last_mpll_cfg = None
162 last_rate = None
163
164 for rate in xrange(13500000, 600001000, 1000):
165 for8bpp = make_mpll(rate, 8)
166 for10bpp = make_mpll(rate, 10)
167 for12bpp = make_mpll(rate, 12)
168
169 mpll_cfg = (for8bpp, for10bpp, for12bpp)
170 if (mpll_cfg != last_mpll_cfg) and (last_rate is not None):
171 mpll_cfg_table[last_rate] = last_mpll_cfg
172
173 last_rate = rate
174 last_mpll_cfg = mpll_cfg
175
176 mpll_cfg_table[last_rate] = last_mpll_cfg
177
178 print "\t",
179 for rate in sorted(mpll_cfg_table.keys()):
180 print ("{\n"
181 "\t\t%d, {\n"
182 "\t\t\t{ %#06x, %#06x },\n"
183 "\t\t\t{ %#06x, %#06x },\n"
184 "\t\t\t{ %#06x, %#06x },\n"
185 "\t\t},\n"
186 "\t}, ") % (
187 rate,
188 mpll_cfg_table[rate][0][0], mpll_cfg_table[rate][0][1],
189 mpll_cfg_table[rate][1][0], mpll_cfg_table[rate][1][1],
190 mpll_cfg_table[rate][2][0], mpll_cfg_table[rate][2][1]),
191 print
192
193def CLK_SLOP(clk): return ((clk) / 1000)
194def CLK_PLUS_SLOP(clk): return ((clk) + CLK_SLOP(clk))
195def CLK_MINUS_SLOP(clk): return ((clk) - CLK_SLOP(clk))
196
197def make_cur_ctr(rate, depth, pixel_rep=0):
198 assert pixel_rep == 0, "Untested with non-zero pixel rep and probably wrong"
199
200 tmds = (rate * depth) / 8. * (pixel_rep + 1)
201
202 adjust_for_jittery_pll = True
203
204 # If the PIXEL clock (not the TMDS rate) is using the special 594 PLL
205 # and is slow enough, we can use normal rates...
206 if ((CLK_MINUS_SLOP(74250000) <= rate <= CLK_PLUS_SLOP(74250000)) or
207 (CLK_MINUS_SLOP(148500000) <= rate <= CLK_PLUS_SLOP(148500000))):
208 adjust_for_jittery_pll = False
209
210 # If rate is slow enough then our jitter isn't a huge issue.
211 # ...allowable clock jitter is 362.3 or higher and we're OK there w/ plenty of
212 # margin as long as we're careful about our PLL settings.
213 if rate <= 79000000:
214 adjust_for_jittery_pll = False
215
216 if not adjust_for_jittery_pll:
217 # This is as documented
218 if tmds <= RATE1: # 46000000
219 return 0x18
220 elif tmds <= RATE2: # 92000000
221 return 0x28
222
223 # I have no idea why the below is true, but it is the simplest rule I could
224 # come up with that matched the tables...
225 if depth == 8:
226 if tmds <= 340000000:
227 # HDMI 1.4
228 return 0x38
229 # HDMI 2.0
230 return 0x18
231 elif depth == 16:
232 if tmds < 576000000:
233 return 0x38
234 return 0x28
235 else:
236 return 0x38
237
238 # The output of rk3288 PLL is the source of the HDMI's MPLL. Apparently
239 # the rk3288 PLL is too jittery. We can lower the PLL bandwidth of MPLL
240 # to compensate.
241 #
242 # Where possible, we try to use the MPLL bandwidth suggested by Synopsis
243 # and we just use lower bandwidth when testing has shown that it's needed.
244 # We try to stick to 0x28 and 0x18 since those numbers are present in
245 # Synopsis tables. We go down to 0x08 if needed and finally to 0x00.
246
247 if rate <= 79000000:
248 # Supposed to be 0x28 here, but we'll do 0x18 to reduce jitter
249 return 0x18
250 elif rate <= 118000000:
251 # Supposed to be 0x28/0x38 here, but we'll do 0x08 to reduce jitter
252 return 0x08
253 # Any higher clock rates go to bandwidth = 0
254 return 0
255
256def do_curr_ctrl_loop():
257 cur_ctrl_table = {}
258 last_cur_ctrl = None
259 last_rate = None
260
261 for rate in xrange(13500000, 600001000, 1000):
262 for8bpp = make_cur_ctr(rate, 8)
263 for10bpp = make_cur_ctr(rate, 10)
264 for12bpp = make_cur_ctr(rate, 12)
265
266 cur_ctrl = (for8bpp, for10bpp, for12bpp)
267 if (cur_ctrl != last_cur_ctrl) and (last_rate is not None):
268 cur_ctrl_table[last_rate] = last_cur_ctrl
269
270 last_rate = rate
271 last_cur_ctrl = cur_ctrl
272
273 cur_ctrl_table[last_rate] = last_cur_ctrl
274
275 print "\t",
276 for rate in sorted(cur_ctrl_table.keys()):
277 print ("{\n"
278 "\t\t%d, { %#06x, %#06x, %#06x },\n"
279 "\t}, ") % (
280 rate,
281 cur_ctrl_table[rate][0],
282 cur_ctrl_table[rate][1],
283 cur_ctrl_table[rate][2]),
284 print
285
286
287
288# From HDMI spec
289VPH_RXTERM = 3.3
290RXTERM = 50
291
292def get_phy_preemphasis(symon, traon, trbon):
293 if (symon, traon, trbon) == (0, 0, 0):
294 assert False, "Not valid?"
295 elif (symon, traon, trbon) == (1, 0, 0):
296 preemph = 0.00
297 elif (symon, traon, trbon) == (1, 0, 1):
298 # Numbers match examples better if I assume .25 / 3 rather than .08
299 preemph = 0.25 / 3
300 elif (symon, traon, trbon) == (1, 1, 0):
301 # Numbers match examples better if I assume .50 / 3 rather than .17
302 preemph = 0.50 / 3
303 elif (symon, traon, trbon) == (1, 1, 1):
304 preemph = 0.25
305 else:
306 assert False, "Not valid"
307
308 return preemph
309
310def phy_lvl_to_voltages(lvl, preemph, rterm):
311 v_lo = VPH_RXTERM - (.772 - 0.01405 * lvl)
312 v_swing = ((VPH_RXTERM - v_lo) * (1 - preemph) /
313 (1 + (RXTERM * (1 + preemph)) / (2 * rterm)))
314 v_hi = v_lo + v_swing
315
316 return v_lo, v_swing, v_hi
317
318def print_phy_config(symbol, term, vlev):
319 ck_symon = bool(symbol & (1 << 0))
320 tx_trbon = bool(symbol & (1 << 1))
321 tx_traon = bool(symbol & (1 << 2))
322 tx_symon = bool(symbol & (1 << 3))
323
324 slopeboost = {
325 0: "no slope boost",
326 1: " 5-10% decrease on TMDS rise/fall times",
327 2: "10-20% decrease on TMDS rise/fall times",
328 3: "20-35% decrease on TMDS rise/fall times",
329 }[(symbol >> 4) & 0x3]
330
331 override = bool(symbol & (1 << 15))
332
333 rterm = (50, 57.14, 66.67, 80, 100, 133, 200)[term]
334
335 sup_ck_lvl = (vlev >> 0) & 0x1f
336 sup_tx_lvl = (vlev >> 5) & 0x1f
337
338 preemph = get_phy_preemphasis(tx_symon, tx_traon, tx_trbon)
339
340 print "symbol=%#06x, term=%#06x, vlev=%#06x" % (symbol, term, vlev)
341 for name, lvl in [("ck", sup_ck_lvl), ("tx", sup_tx_lvl)]:
342 v_lo, v_swing, v_hi = phy_lvl_to_voltages(lvl, preemph, rterm)
343 print " %s: lvl = %2d, term=%3d, vlo = %.2f, vhi=%.2f, vswing = %.2f, %s" % (
344 name, lvl, rterm, v_lo, v_hi, v_swing, slopeboost)
345
346#def calc_ideal_phy_lvl(swing_mv, preemph, rterm):
347 #"""Get the ideal "lvl" for the given swing, preemph, and termination.
348
349 #This might not be integral, but and might not fit the 0-31 range.
350 #"""
351 #v_lo = (VPH_RXTERM -
352 #v_swing / (1 - preemph) -
353 #(v_swing * RXTERM * (1 + preemph)) / (2 * rterm * (1 - preemph)))
354 #lvl = (.772 - (VPH_RXTERM - v_lo)) / 0.01405
355
356 #return lvl
357
358def do_phy_config_list(rate=16500000):
359 # From HDMI spec
360 VPH_RXTERM = 3.3
361 RXTERM = 50
362
363 # Set to True to print even things that don't meet requirements.
364 print_invalid = False
365
366 # Totally a guess based on what's in IMX6DQRM
367 if rate <= 165000000:
368 symon = 1 # tx_symon
369 traon = 0 # tx_traon
370 trbon = 0 # tx_trbon
371 else:
372 symon = 1 # tx_symon
373 traon = 0 # tx_traon
374 trbon = 1 # tx_trbon
375
376 print "Guessing symon, traon, trbon based on rate: (%d, %d, %d)" % (
377 symon, traon, trbon)
378
379 preemph = get_phy_preemphasis(symon, traon, trbon)
380
381 # Notes:
382 # - swing needs to be between .4 and .6
383 # - If <= 165MHz, vhi is (VPH_RXTERM + .01) thru (VPH_RXTERM - .01)
384 # - If > 165MHz, vhi is (VPH_RXTERM + .01) thru (VPH_RXTERM - .2)
385 # - If <= 165MHz, vlo is (VPH_RXTERM - .4) thru (VPH_RXTERM - .6)
386 # - If > 165MHz, vlo is (VPH_RXTERM - .4) thru (VPH_RXTERM - .7)
387 #
388 # TODO: I'm not sure we can actually reach vhi of 3.3 +/- .01
389 # TODO: How do we pick amongst all of these?
390
391 if rate <= 165000000:
392 v_hi_min = 3.19 # Should be (VPH_RXTERM - .01), but not possible?
393 v_hi_max = (VPH_RXTERM + .01)
394 v_lo_min = (VPH_RXTERM - .6)
395 v_lo_max = (VPH_RXTERM - .4)
396 else:
397 v_hi_min = (VPH_RXTERM - .2)
398 v_hi_max = (VPH_RXTERM + .01)
399 v_lo_min = (VPH_RXTERM - .7)
400 v_lo_max = (VPH_RXTERM - .4)
401
402 for lvl in xrange(0, 31):
403 for rterm in (50, 57.14, 66.67, 80, 100, 133, 200):
404 v_lo, v_swing, v_hi = phy_lvl_to_voltages(lvl, preemph, rterm)
405
406 if (print_invalid or
407 ((.4 <= v_swing <= .6) and
408 (v_hi_min <= v_hi <= v_hi_max) and
409 (v_lo_min <= v_lo <= v_lo_max))):
410 print "lvl = %2d, term=%3d, vlo = %.2f, vhi=%.2f, vswing = %.2f" % (
411 lvl, rterm, v_lo, v_hi, v_swing)
412
413# Examples:
414#
415# $ ./rk3288_hdmitables.py mpll
416# $ ./rk3288_hdmitables.py curr
417# $ ./rk3288_hdmitables.py phy_list 165000000
418# $ ./rk3288_hdmitables.py phy_list 600000000
419# $ ./rk3288_hdmitables.py phy_print 0x8009, 0x0005, 0x01ad
420
421def main(todo, *args):
422 if todo == "mpll":
423 do_mpll_loop()
424 elif todo == "curr":
425 do_curr_ctrl_loop()
426 elif todo == "phy_list":
427 (rate,) = args
428 rate = int(rate, 0)
429 do_phy_config_list(rate)
430 elif todo == "phy_print":
431 (symbol, term, vlev) = args
432 symbol = int(symbol.rstrip(","), 0)
433 term = int(term.rstrip(","), 0)
434 vlev = int(vlev.rstrip(","), 0)
435
436 print_phy_config(symbol, term, vlev)
437
438 # These ought to match the tables in the docs. They are close, but not
439 # perfect. ...but my math is definitely right since v_lo doesn't match
440 # and v_lo should be very simple. Even if we try to find more significant
441 # digits for 0.772 and 0.01405 we still can't make it match, so I'm assuming
442 # that they rounded somewhere in their math...
443
444 #print "%.3f, %.3f, %.3f" % phy_lvl_to_voltages(19, get_phy_preemphasis(1, 0, 0), 100)
445 #print "%.3f, %.3f, %.3f" % phy_lvl_to_voltages(10, get_phy_preemphasis(1, 0, 0), 100)
446 #print "%.3f, %.3f, %.3f" % phy_lvl_to_voltages( 6, get_phy_preemphasis(1, 0, 1), 100)
447
448 #print "%.3f, %.3f, %.3f" % phy_lvl_to_voltages(21, get_phy_preemphasis(1, 0, 0), 133)
449 #print "%.3f, %.3f, %.3f" % phy_lvl_to_voltages(13, get_phy_preemphasis(1, 0, 0), 133)
450 #print "%.3f, %.3f, %.3f" % phy_lvl_to_voltages( 8, get_phy_preemphasis(1, 0, 1), 133)
451
452
453if __name__ == '__main__':
454 main(*sys.argv[1:])