[v4,1/2] video: hdmi: add helper functions for N and CTS
Doug Anderson
dianders at chromium.org
Tue Jun 7 15:27:02 UTC 2016
Arnaud,
On Tue, Jun 7, 2016 at 1:41 AM, Arnaud Pouliquen
<arnaud.pouliquen at st.com> wrote:
> hi Doug,
>
> Thanks for this very interesting feed back.
>
> On my side i'm quite busy on some other topics, and on my platform,
> CTS is hardware computed.
> So if you have the experience and the hardware for coherent N and CTS
> calculations, you are welcome to improve my patch.
Sure. I'm not working on anything related to this at the moment, but
I happened to stumble upon your patch and I figured I'd at least post
my observations / history in case it was useful to anyone. I know it
took me quite some time before I understood where all these magic
numbers came from and I figured I'd save others the effort.
If nobody has improved this by the next time I end up working on it, I
will definitely post patches.
> On 06/06/2016 06:34 PM, Doug Anderson wrote:
>> Hi,
>>
>> On Thu, Apr 21, 2016 at 8:29 AM, Arnaud Pouliquen <dianders at chromium.org> 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?
> I discussed with Daniel Vetter on DRM IRC, he requests more
> adherence/commitment on it. So if you are interested in using helpers in
> your driver that should help :-)
I'm not actively working on any drivers that would use this. In the
past I had to dive deep into dw_hdmi on Rockchip SoCs to help fix a
bunch of bugs, but it's not something I usually work on. I would have
posted my changes upstream but we have enough non-upstream stuff in
our dw_hdmi code that it was difficult to really do that. Hopefully
next time around...
In general, though, I would support this going someplace common so we
didn't need to keep reinventing it. It seems like I've seen this same
code several times...
>> 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;
>> }
> Right, I have based my default case algorithm, on HDMI recommendation,
>> + val = (u64)tmds_clk * n_cts->n;
>> + n_cts->cts = div64_u64(val, 128UL * audio_fs);
> but yours seems more accurate. if too slow, a parameter could allows to
> select between accurate and fast calculation...
Yeah, the HDMI docs I found didn't totally explain what we were trying
to accomplish with all their magic numbers and I agree that they
suggested just falling back as you say.
My calculations, of course, assume we know the real clock and not just
the integral-rounded version of it...
>>> + /*
>>> + * 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.
> This fallback that provides a ratio between the use of the CTS and
> (CTS+1) value was proposed by Russell, when no CTS accurate value is
> found. I think it is also interesting to keep it, in addition of your
> algorithm.
> This is another way to allow driver to implement a compensation, to
> avoid audio cut.
Ah, that actually makes tons of sense. Thanks! Yeah, then I agree
this is a good idea.
-Doug
More information about the dri-devel
mailing list