Hi, On Thu, Apr 21, 2016 at 8:29 AM, Arnaud Pouliquen <dianders@xxxxxxxxxxxx> wrote: > Add helper functions to compute HDMI CTS and N parameters. > Implementation is based on HDMI 1.4b specification. It would be super nice to have this somewhere common. Any idea who would land this? > +static const struct hdmi_audio_acr hdmi_audio_standard_acr[3][13] = { > + [HDMI_AUDIO_N_CTS_32KHZ] = { > + /* N and CTS values for 32 kHz rate*/ > + { 25174825, { 4576, 28125, 0 } }, /* 25.20/1.001 MHz */ > + { 25200000, { 4096, 25200, 0 } }, /* 25.20 MHz */ > + { 27000000, { 4096, 27000, 0 } }, /* 27.00 MHz */ > + { 27027000, { 4096, 27027, 0 } }, /* 27.00*1.001 MHz */ > + { 54000000, { 4096, 54000, 0 } }, /* 54.00 MHz */ > + { 54054000, { 4096, 54054, 0 } }, /* 54.00*1.001 MHz */ > + { 74175824, { 11648, 210937, 50 } }, /* 74.25/1.001 MHz */ > + { 74250000, { 4096, 74250, 0 } }, /* 74.25 MHz */ > + { 148351648, { 11648, 421875, 0 } }, /* 148.50/1.001 MHz */ > + { 148500000, { 4096, 148500, 0 } }, /* 148.50 MHz */ > + { 296703296, { 5824, 421875, 0 } }, /* 297/1.001 MHz (truncated)*/ > + { 296703297, { 5824, 421875, 0 } }, /* 297/1.001 MHz (rounded)*/ > + { 297000000, { 3072, 222750, 0 } }, /* 297 MHz */ One thing to note is that for all but the non-integral clock rates and the rates >= ~297MHz, all of this can be done programmatically. ...the function I came up with to do that is pretty slow, so a table is still useful in general unless you want to try to optimize things, but it might be nice to have the function available as a fallback? Specifically many TVs will allow audio to work with rates other than the ones in the HDMI spec. You can see the full implementation we used on some devices I worked on at <https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-3.14/drivers/gpu/drm/bridge/dw_hdmi.c>. Specifically the function for computing N: static unsigned int hdmi_compute_n(struct dw_hdmi *hdmi, unsigned long pixel_clk) { unsigned int freq = hdmi->sample_rate; unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500); unsigned int max_n = (128 * freq) / 300; unsigned int ideal_n = (128 * freq) / 1000; unsigned int best_n_distance = ideal_n; unsigned int best_n = 0; u64 best_diff = U64_MAX; int n; /* If the ideal N could satisfy the audio math, then just take it */ if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0) return ideal_n; for (n = min_n; n <= max_n; n++) { u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk); if (diff < best_diff || (diff == best_diff && abs(n - ideal_n) < best_n_distance)) { best_n = n; best_diff = diff; best_n_distance = abs(best_n - ideal_n); } /* * The best N already satisfy the audio math, and also be * the closest value to ideal N, so just cut the loop. */ if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance)) break; } return best_n; } I believe this function written by Yakir Yang based on a bit of python I had coded up. The python has the advantage that it will come up with the right N/CTS even for fractional clock rates, like 25.20/1.001: def DIV_ROUND_UP(x, y): return (x + y - 1) / y def calc(freq, tmds): min_n = DIV_ROUND_UP((128 * freq), 1500) max_n = (128 * freq) / 300 ideal_n = (128 * freq) / 1000 best = 0xffffffffffffffff for n in xrange(min_n, max_n + 1): cts = int(round((tmds * n / (128. * freq)))) diff = abs(tmds * n - cts * (128. * freq)) if (diff < best) or \ (diff == best and abs(n - ideal_n) < abs(best_n - ideal_n)): best = diff best_n = n # Want a number that's close to an integer here print tmds, freq, best_n, tmds * (best_n) / (128. * freq) n = best_n cts = (tmds * n) / (128 * freq) print ">>> ((128 * %d) * %d) / %d." % (freq, cts, n) print "%f" % (((128 * freq) * cts) / n) print 25174825.1748 32000 4576 28125.0 >>> ((128 * 32000) * 28125) / 4576. 25174825.174825 25174825.1748 44100 7007 31250.0 >>> ((128 * 44100) * 31250) / 7007. 25174825.174825 25174825.1748 48000 6864 28125.0 >>> ((128 * 48000) * 28125) / 6864. 25174825.174825 One other thing to note is that if your HDMI block doesn't happen to make _exactly_ the right clock then these values aren't right. For instance, if you end up making 25174825 Hz instead of 25200000 / 1.001 Hz that different N/CTS values are ideal. The numbers below are the result of my python but (as you can see) things don't match up properly. 25174825 32000 4405 27074.0000305 >>> ((128 * 32000) * 27074) / 4405. 25174824.000000 25174825 44100 9073 40464.0000044 >>> ((128 * 44100) * 40464) / 9073. 25174824.000000 25174825 48000 15503 63522.9999959 >>> ((128 * 48000) * 63522) / 15503. 25174428.000000 In my particular case we could make 25,176,471 which we thought was close enough to the proper clock rate, but still deserves better N/CTS rates. /* 25176471 for 25.175 MHz = 428000000 / 17. */ { .tmds = 25177000, .n_32k = 4352, .n_44k1 = 14994, .n_48k = 6528, }, > + /* > + * Pre-defined frequency not found. Compute CTS using formula: > + * CTS = (Ftdms_clk * N) / (128 * audio_fs) > + */ > + val = (u64)tmds_clk * n_cts->n; > + n_cts->cts = div64_u64(val, 128UL * audio_fs); > + > + n_cts->cts_1_ratio = 0; > + min = (u64)n_cts->cts * 128UL * audio_fs; > + if (min < val) { > + /* > + * Non-accurate value for CTS > + * compute ratio, needed by user to alternate in ACR > + * between CTS and CTS + 1 value. > + */ > + n_cts->cts_1_ratio = ((u32)(val - min)) * 100 / > + (128 * audio_fs); > + } This fallback isn't nearly as nice and will likely lead to audio reconstitution problems. IIRC the problem was periodic audio cutouts of you listened long enough. -Doug _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel