[v4,1/2] video: hdmi: add helper functions for N and CTS

Submitted by Arnaud Pouliquen on April 21, 2016, 3:29 p.m.

Details

Message ID 1461252554-16522-2-git-send-email-arnaud.pouliquen@st.com
State New
Headers show
Series "sti: add audio interface to the hdmi driver" ( rev: 2 ) in DRI devel

Not browsing as part of any series.

Commit Message

Arnaud Pouliquen April 21, 2016, 3:29 p.m.
Add helper functions to compute HDMI CTS and N parameters.
Implementation is based on HDMI 1.4b specification.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Acked-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Acked-by: Vincent ABRIOU <vincent.abriou@st.com>
---
 drivers/video/hdmi.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/hdmi.h |  24 ++++++
 2 files changed, 232 insertions(+)

Patch hide | download patch | download mbox

diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 1626892..5d124ef 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -1242,3 +1242,211 @@  int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer)
 	return ret;
 }
 EXPORT_SYMBOL(hdmi_infoframe_unpack);
+
+/*
+ * audio clock regeneration (acr) parameters
+ * N and CTS computation are based on HDMI specification 1.4b
+ */
+enum hdmi_audio_rate {
+	HDMI_AUDIO_N_CTS_32KHZ,
+	HDMI_AUDIO_N_CTS_44_1KHZ,
+	HDMI_AUDIO_N_CTS_48KHZ,
+};
+
+struct hdmi_audio_acr {
+	unsigned int tmds_clk;
+	struct hdmi_audio_n_cts n_cts;
+};
+
+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 */
+	},
+	[HDMI_AUDIO_N_CTS_44_1KHZ] = {
+		/* N and CTS values for 44.1 kHz, 88.2 kHz and 176.4 kHz rates*/
+		{  25174825, {  7007,  31250, 0 } }, /* 25.20/1.001  MHz */
+		{  25200000, {  6272,  28000, 0 } }, /* 25.20        MHz */
+		{  27000000, {  6272,  30000, 0 } }, /* 27.00        MHz */
+		{  27027000, {  6272,  30030, 0 } }, /* 27.00*1.001  MHz */
+		{  54000000, {  6272,  60000, 0 } }, /* 54.00        MHz */
+		{  54054000, {  6272,  60060, 0 } }, /* 54.00*1.001  MHz */
+		{  74175824, { 17836, 234375, 0 } }, /* 74.25/1.001  MHz */
+		{  74250000, {  6272,  82500, 0 } }, /* 74.25        MHz */
+		{ 148351648, {  8918, 234375, 0 } }, /* 148.50/1.001 MHz */
+		{ 148500000, {  6272, 165000, 0 } }, /* 148.50       MHz */
+		{ 296703296, {  4459, 234375, 0 } }, /* 297/1.001 MHz (truncated) */
+		{ 296703297, {  4459, 234375, 0 } }, /* 297/1.001 MHz (rounded) */
+		{ 297000000, {  4704, 247500, 0 } }, /* 297          MHz */
+	},
+	[HDMI_AUDIO_N_CTS_48KHZ] = {
+		/* N and CTS values for 48 kHz, 96 kHz and 192 kHz rates*/
+		{  25174825, {  6864,  28125, 0 } }, /* 25.20/1.001  MHz */
+		{  25200000, {  6144,  25200, 0 } }, /* 25.20        MHz */
+		{  27000000, {  6144,  27000, 0 } }, /* 27.00        MHz */
+		{  27027000, {  6144,  27027, 0 } }, /* 27.00*1.001  MHz */
+		{  54000000, {  6144,  54000, 0 } }, /* 54.00        MHz */
+		{  54054000, {  6144,  54054, 0 } }, /* 54.00*1.001  MHz */
+		{  74175824, { 11648, 140625, 0 } }, /* 74.25/1.001  MHz */
+		{  74250000, {  6144,  74250, 0 } }, /* 74.25        MHz */
+		{ 148351648, {  5824, 140625, 0 } }, /* 148.50/1.001 MHz */
+		{ 148500000, {  6144, 148500, 0 } }, /* 148.50       MHz */
+		{ 296703296, {  5824, 281250, 0 } }, /* 297/1.001 MHz (truncated) */
+		{ 296703297, {  5824, 281250, 0 } }, /* 297/1.001 MHz (rounded) */
+		{ 297000000, {  5120, 247500, 0 } }, /* 297          MHz */
+	}
+};
+
+/**
+ * hdmi_audio_get_coherent_n_cts() - compute N and CTS parameters for coherent
+ * clocks. Coherent clock means that audio and TMDS clocks have the same
+ * source (no drifts between clocks).
+ *
+ * @audio_fs: audio frame clock frequency in Hz
+ * @tmds_clk: HDMI TMDS clock frequency in Hz
+ * @n_cts: N and CTS parameter returned to user
+ *
+ * Values computed are based on table described in HDMI specification 1.4b
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_audio_get_coherent_n_cts(unsigned int audio_fs,
+				  unsigned int tmds_clk,
+				  struct hdmi_audio_n_cts *n_cts)
+{
+	int audio_freq_id, i;
+	int rate_coeff = 1;
+	u64 val, min;
+	const struct hdmi_audio_acr  *acr_table;
+	const struct hdmi_audio_n_cts *predef_n_cts = NULL;
+
+	switch (audio_fs) {
+	case 32000:
+		audio_freq_id = HDMI_AUDIO_N_CTS_32KHZ;
+		n_cts->n = 4096;
+		break;
+	case 44100:
+		audio_freq_id = HDMI_AUDIO_N_CTS_44_1KHZ;
+		n_cts->n = 6272;
+		break;
+	case 48000:
+		audio_freq_id = HDMI_AUDIO_N_CTS_48KHZ;
+		n_cts->n = 6144;
+		break;
+	case 88200:
+		audio_freq_id = HDMI_AUDIO_N_CTS_44_1KHZ;
+		rate_coeff = 2;
+		n_cts->n = 6272 * 2;
+		break;
+	case 96000:
+		audio_freq_id = HDMI_AUDIO_N_CTS_48KHZ;
+		rate_coeff = 2;
+		n_cts->n = 6144 * 2;
+		break;
+	case 176400:
+		audio_freq_id = HDMI_AUDIO_N_CTS_44_1KHZ;
+		rate_coeff = 4;
+		n_cts->n = 6272 * 4;
+		break;
+	case 192000:
+		audio_freq_id = HDMI_AUDIO_N_CTS_48KHZ;
+		rate_coeff = 4;
+		n_cts->n = 6144 * 4;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	acr_table = hdmi_audio_standard_acr[audio_freq_id];
+	for (i = 0; i < ARRAY_SIZE(hdmi_audio_standard_acr[0]); i++) {
+		if (tmds_clk == acr_table[i].tmds_clk) {
+			predef_n_cts = &acr_table[i].n_cts;
+			n_cts->n = predef_n_cts->n * rate_coeff;
+			n_cts->cts = predef_n_cts->cts;
+			n_cts->cts_1_ratio = predef_n_cts->cts_1_ratio;
+			return 0;
+		}
+	}
+
+	/*
+	 * 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);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(hdmi_audio_get_coherent_n_cts);
+
+/**
+ * hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent
+ * clocks. None-coherent clocks means that audio and TMDS clocks have not the
+ * same source (drifts between clocks). In this case assumption is that CTS is
+ * automatically calculated by hardware.
+ *
+ * @audio_fs: audio frame clock frequency in Hz
+ *
+ * Values computed are based on table described in HDMI specification 1.4b
+ *
+ * Returns n value.
+ */
+int hdmi_audio_get_non_coherent_n(unsigned int audio_fs)
+{
+	unsigned int n;
+
+	switch (audio_fs) {
+	case 32000:
+		n = 4096;
+		break;
+	case 44100:
+		n = 6272;
+		break;
+	case 48000:
+		n = 6144;
+		break;
+	case 88200:
+		n = 6272 * 2;
+		break;
+	case 96000:
+		n = 6144 * 2;
+		break;
+	case 176400:
+		n = 6272 * 4;
+		break;
+	case 192000:
+		n = 6144 * 4;
+		break;
+	default:
+		/* Not pre-defined, recommended value: 128 * fs / 1000 */
+		n = (audio_fs * 128) / 1000;
+	}
+
+	return n;
+}
+EXPORT_SYMBOL(hdmi_audio_get_non_coherent_n);
+
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index e974420..088d09f8 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -333,4 +333,28 @@  int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer);
 void hdmi_infoframe_log(const char *level, struct device *dev,
 			union hdmi_infoframe *frame);
 
+/**
+ * struct hdmi_audio_n_cts - n and cts parameter for ACR packets
+ * @n: N parameter
+ * @cts: CTS parameter
+ * @cts_1_ratio: ratio from 0 to 99 to alternate "CTS" and "CTS + 1" values
+ *  ratio = 0: CTS parameter is accurate, no need to alternate with "CTS + 1"
+ *             value
+ *  ratio = x: Need to alternate with ACR "CTS + 1" value x percent of the time
+ *             to generate accurate audio clock
+ *  as exemple: if cts_1_ratio = 30: to have an accurate value, user
+ *  should transfer CTS value 70% of the time and (CTS+1) value 30% of the time
+ */
+struct hdmi_audio_n_cts {
+	unsigned int n;
+	unsigned int cts;
+	unsigned int cts_1_ratio;
+};
+
+int hdmi_audio_get_coherent_n_cts(unsigned int audio_fs,
+				  unsigned int tmds_clk,
+				  struct hdmi_audio_n_cts *n_cts);
+
+int hdmi_audio_get_non_coherent_n(unsigned int audio_fs);
+
 #endif /* _DRM_HDMI_H */

Comments

Add linux-fbdev diffusion list in loop for patch-set review.

On 04/21/2016 05:29 PM, Arnaud POULIQUEN wrote:
> Add helper functions to compute HDMI CTS and N parameters.
> Implementation is based on HDMI 1.4b specification.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Acked-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
> Acked-by: Vincent ABRIOU <vincent.abriou@st.com>
> ---
>  drivers/video/hdmi.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/hdmi.h |  24 ++++++
>  2 files changed, 232 insertions(+)
> 
> diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
> index 1626892..5d124ef 100644
> --- a/drivers/video/hdmi.c
> +++ b/drivers/video/hdmi.c
> @@ -1242,3 +1242,211 @@ int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer)
>  	return ret;
>  }
>  EXPORT_SYMBOL(hdmi_infoframe_unpack);
> +
> +/*
> + * audio clock regeneration (acr) parameters
> + * N and CTS computation are based on HDMI specification 1.4b
> + */
> +enum hdmi_audio_rate {
> +	HDMI_AUDIO_N_CTS_32KHZ,
> +	HDMI_AUDIO_N_CTS_44_1KHZ,
> +	HDMI_AUDIO_N_CTS_48KHZ,
> +};
> +
> +struct hdmi_audio_acr {
> +	unsigned int tmds_clk;
> +	struct hdmi_audio_n_cts n_cts;
> +};
> +
> +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 */
> +	},
> +	[HDMI_AUDIO_N_CTS_44_1KHZ] = {
> +		/* N and CTS values for 44.1 kHz, 88.2 kHz and 176.4 kHz rates*/
> +		{  25174825, {  7007,  31250, 0 } }, /* 25.20/1.001  MHz */
> +		{  25200000, {  6272,  28000, 0 } }, /* 25.20        MHz */
> +		{  27000000, {  6272,  30000, 0 } }, /* 27.00        MHz */
> +		{  27027000, {  6272,  30030, 0 } }, /* 27.00*1.001  MHz */
> +		{  54000000, {  6272,  60000, 0 } }, /* 54.00        MHz */
> +		{  54054000, {  6272,  60060, 0 } }, /* 54.00*1.001  MHz */
> +		{  74175824, { 17836, 234375, 0 } }, /* 74.25/1.001  MHz */
> +		{  74250000, {  6272,  82500, 0 } }, /* 74.25        MHz */
> +		{ 148351648, {  8918, 234375, 0 } }, /* 148.50/1.001 MHz */
> +		{ 148500000, {  6272, 165000, 0 } }, /* 148.50       MHz */
> +		{ 296703296, {  4459, 234375, 0 } }, /* 297/1.001 MHz (truncated) */
> +		{ 296703297, {  4459, 234375, 0 } }, /* 297/1.001 MHz (rounded) */
> +		{ 297000000, {  4704, 247500, 0 } }, /* 297          MHz */
> +	},
> +	[HDMI_AUDIO_N_CTS_48KHZ] = {
> +		/* N and CTS values for 48 kHz, 96 kHz and 192 kHz rates*/
> +		{  25174825, {  6864,  28125, 0 } }, /* 25.20/1.001  MHz */
> +		{  25200000, {  6144,  25200, 0 } }, /* 25.20        MHz */
> +		{  27000000, {  6144,  27000, 0 } }, /* 27.00        MHz */
> +		{  27027000, {  6144,  27027, 0 } }, /* 27.00*1.001  MHz */
> +		{  54000000, {  6144,  54000, 0 } }, /* 54.00        MHz */
> +		{  54054000, {  6144,  54054, 0 } }, /* 54.00*1.001  MHz */
> +		{  74175824, { 11648, 140625, 0 } }, /* 74.25/1.001  MHz */
> +		{  74250000, {  6144,  74250, 0 } }, /* 74.25        MHz */
> +		{ 148351648, {  5824, 140625, 0 } }, /* 148.50/1.001 MHz */
> +		{ 148500000, {  6144, 148500, 0 } }, /* 148.50       MHz */
> +		{ 296703296, {  5824, 281250, 0 } }, /* 297/1.001 MHz (truncated) */
> +		{ 296703297, {  5824, 281250, 0 } }, /* 297/1.001 MHz (rounded) */
> +		{ 297000000, {  5120, 247500, 0 } }, /* 297          MHz */
> +	}
> +};
> +
> +/**
> + * hdmi_audio_get_coherent_n_cts() - compute N and CTS parameters for coherent
> + * clocks. Coherent clock means that audio and TMDS clocks have the same
> + * source (no drifts between clocks).
> + *
> + * @audio_fs: audio frame clock frequency in Hz
> + * @tmds_clk: HDMI TMDS clock frequency in Hz
> + * @n_cts: N and CTS parameter returned to user
> + *
> + * Values computed are based on table described in HDMI specification 1.4b
> + *
> + * Returns 0 on success or a negative error code on failure.
> + */
> +int hdmi_audio_get_coherent_n_cts(unsigned int audio_fs,
> +				  unsigned int tmds_clk,
> +				  struct hdmi_audio_n_cts *n_cts)
> +{
> +	int audio_freq_id, i;
> +	int rate_coeff = 1;
> +	u64 val, min;
> +	const struct hdmi_audio_acr  *acr_table;
> +	const struct hdmi_audio_n_cts *predef_n_cts = NULL;
> +
> +	switch (audio_fs) {
> +	case 32000:
> +		audio_freq_id = HDMI_AUDIO_N_CTS_32KHZ;
> +		n_cts->n = 4096;
> +		break;
> +	case 44100:
> +		audio_freq_id = HDMI_AUDIO_N_CTS_44_1KHZ;
> +		n_cts->n = 6272;
> +		break;
> +	case 48000:
> +		audio_freq_id = HDMI_AUDIO_N_CTS_48KHZ;
> +		n_cts->n = 6144;
> +		break;
> +	case 88200:
> +		audio_freq_id = HDMI_AUDIO_N_CTS_44_1KHZ;
> +		rate_coeff = 2;
> +		n_cts->n = 6272 * 2;
> +		break;
> +	case 96000:
> +		audio_freq_id = HDMI_AUDIO_N_CTS_48KHZ;
> +		rate_coeff = 2;
> +		n_cts->n = 6144 * 2;
> +		break;
> +	case 176400:
> +		audio_freq_id = HDMI_AUDIO_N_CTS_44_1KHZ;
> +		rate_coeff = 4;
> +		n_cts->n = 6272 * 4;
> +		break;
> +	case 192000:
> +		audio_freq_id = HDMI_AUDIO_N_CTS_48KHZ;
> +		rate_coeff = 4;
> +		n_cts->n = 6144 * 4;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	acr_table = hdmi_audio_standard_acr[audio_freq_id];
> +	for (i = 0; i < ARRAY_SIZE(hdmi_audio_standard_acr[0]); i++) {
> +		if (tmds_clk == acr_table[i].tmds_clk) {
> +			predef_n_cts = &acr_table[i].n_cts;
> +			n_cts->n = predef_n_cts->n * rate_coeff;
> +			n_cts->cts = predef_n_cts->cts;
> +			n_cts->cts_1_ratio = predef_n_cts->cts_1_ratio;
> +			return 0;
> +		}
> +	}
> +
> +	/*
> +	 * 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);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(hdmi_audio_get_coherent_n_cts);
> +
> +/**
> + * hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent
> + * clocks. None-coherent clocks means that audio and TMDS clocks have not the
> + * same source (drifts between clocks). In this case assumption is that CTS is
> + * automatically calculated by hardware.
> + *
> + * @audio_fs: audio frame clock frequency in Hz
> + *
> + * Values computed are based on table described in HDMI specification 1.4b
> + *
> + * Returns n value.
> + */
> +int hdmi_audio_get_non_coherent_n(unsigned int audio_fs)
> +{
> +	unsigned int n;
> +
> +	switch (audio_fs) {
> +	case 32000:
> +		n = 4096;
> +		break;
> +	case 44100:
> +		n = 6272;
> +		break;
> +	case 48000:
> +		n = 6144;
> +		break;
> +	case 88200:
> +		n = 6272 * 2;
> +		break;
> +	case 96000:
> +		n = 6144 * 2;
> +		break;
> +	case 176400:
> +		n = 6272 * 4;
> +		break;
> +	case 192000:
> +		n = 6144 * 4;
> +		break;
> +	default:
> +		/* Not pre-defined, recommended value: 128 * fs / 1000 */
> +		n = (audio_fs * 128) / 1000;
> +	}
> +
> +	return n;
> +}
> +EXPORT_SYMBOL(hdmi_audio_get_non_coherent_n);
> +
> diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
> index e974420..088d09f8 100644
> --- a/include/linux/hdmi.h
> +++ b/include/linux/hdmi.h
> @@ -333,4 +333,28 @@ int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer);
>  void hdmi_infoframe_log(const char *level, struct device *dev,
>  			union hdmi_infoframe *frame);
>  
> +/**
> + * struct hdmi_audio_n_cts - n and cts parameter for ACR packets
> + * @n: N parameter
> + * @cts: CTS parameter
> + * @cts_1_ratio: ratio from 0 to 99 to alternate "CTS" and "CTS + 1" values
> + *  ratio = 0: CTS parameter is accurate, no need to alternate with "CTS + 1"
> + *             value
> + *  ratio = x: Need to alternate with ACR "CTS + 1" value x percent of the time
> + *             to generate accurate audio clock
> + *  as exemple: if cts_1_ratio = 30: to have an accurate value, user
> + *  should transfer CTS value 70% of the time and (CTS+1) value 30% of the time
> + */
> +struct hdmi_audio_n_cts {
> +	unsigned int n;
> +	unsigned int cts;
> +	unsigned int cts_1_ratio;
> +};
> +
> +int hdmi_audio_get_coherent_n_cts(unsigned int audio_fs,
> +				  unsigned int tmds_clk,
> +				  struct hdmi_audio_n_cts *n_cts);
> +
> +int hdmi_audio_get_non_coherent_n(unsigned int audio_fs);
> +
>  #endif /* _DRM_HDMI_H */
>
Hello,

As there is no more feedback on this patch, should i suppose that I need
to abandon it?

I can integrate it in my driver... but as, N and CTS calculation is not
platform dependent, this would make sense to add it in a generic part.
(Example of another platform that could use it:
https://patchwork.kernel.org/patch/8887341)

Thanks and regards
Arnaud

On 04/28/2016 02:13 PM, Arnaud Pouliquen wrote:
> Add linux-fbdev diffusion list in loop for patch-set review.
> 
> On 04/21/2016 05:29 PM, Arnaud POULIQUEN wrote:
>> Add helper functions to compute HDMI CTS and N parameters.
>> Implementation is based on HDMI 1.4b specification.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Acked-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
>> Acked-by: Vincent ABRIOU <vincent.abriou@st.com>
>> ---
>>  drivers/video/hdmi.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  include/linux/hdmi.h |  24 ++++++
>>  2 files changed, 232 insertions(+)
>>
>> diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
>> index 1626892..5d124ef 100644
>> --- a/drivers/video/hdmi.c
>> +++ b/drivers/video/hdmi.c
>> @@ -1242,3 +1242,211 @@ int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer)
>>  	return ret;
>>  }
>>  EXPORT_SYMBOL(hdmi_infoframe_unpack);
>> +
>> +/*
>> + * audio clock regeneration (acr) parameters
>> + * N and CTS computation are based on HDMI specification 1.4b
>> + */
>> +enum hdmi_audio_rate {
>> +	HDMI_AUDIO_N_CTS_32KHZ,
>> +	HDMI_AUDIO_N_CTS_44_1KHZ,
>> +	HDMI_AUDIO_N_CTS_48KHZ,
>> +};
>> +
>> +struct hdmi_audio_acr {
>> +	unsigned int tmds_clk;
>> +	struct hdmi_audio_n_cts n_cts;
>> +};
>> +
>> +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 */
>> +	},
>> +	[HDMI_AUDIO_N_CTS_44_1KHZ] = {
>> +		/* N and CTS values for 44.1 kHz, 88.2 kHz and 176.4 kHz rates*/
>> +		{  25174825, {  7007,  31250, 0 } }, /* 25.20/1.001  MHz */
>> +		{  25200000, {  6272,  28000, 0 } }, /* 25.20        MHz */
>> +		{  27000000, {  6272,  30000, 0 } }, /* 27.00        MHz */
>> +		{  27027000, {  6272,  30030, 0 } }, /* 27.00*1.001  MHz */
>> +		{  54000000, {  6272,  60000, 0 } }, /* 54.00        MHz */
>> +		{  54054000, {  6272,  60060, 0 } }, /* 54.00*1.001  MHz */
>> +		{  74175824, { 17836, 234375, 0 } }, /* 74.25/1.001  MHz */
>> +		{  74250000, {  6272,  82500, 0 } }, /* 74.25        MHz */
>> +		{ 148351648, {  8918, 234375, 0 } }, /* 148.50/1.001 MHz */
>> +		{ 148500000, {  6272, 165000, 0 } }, /* 148.50       MHz */
>> +		{ 296703296, {  4459, 234375, 0 } }, /* 297/1.001 MHz (truncated) */
>> +		{ 296703297, {  4459, 234375, 0 } }, /* 297/1.001 MHz (rounded) */
>> +		{ 297000000, {  4704, 247500, 0 } }, /* 297          MHz */
>> +	},
>> +	[HDMI_AUDIO_N_CTS_48KHZ] = {
>> +		/* N and CTS values for 48 kHz, 96 kHz and 192 kHz rates*/
>> +		{  25174825, {  6864,  28125, 0 } }, /* 25.20/1.001  MHz */
>> +		{  25200000, {  6144,  25200, 0 } }, /* 25.20        MHz */
>> +		{  27000000, {  6144,  27000, 0 } }, /* 27.00        MHz */
>> +		{  27027000, {  6144,  27027, 0 } }, /* 27.00*1.001  MHz */
>> +		{  54000000, {  6144,  54000, 0 } }, /* 54.00        MHz */
>> +		{  54054000, {  6144,  54054, 0 } }, /* 54.00*1.001  MHz */
>> +		{  74175824, { 11648, 140625, 0 } }, /* 74.25/1.001  MHz */
>> +		{  74250000, {  6144,  74250, 0 } }, /* 74.25        MHz */
>> +		{ 148351648, {  5824, 140625, 0 } }, /* 148.50/1.001 MHz */
>> +		{ 148500000, {  6144, 148500, 0 } }, /* 148.50       MHz */
>> +		{ 296703296, {  5824, 281250, 0 } }, /* 297/1.001 MHz (truncated) */
>> +		{ 296703297, {  5824, 281250, 0 } }, /* 297/1.001 MHz (rounded) */
>> +		{ 297000000, {  5120, 247500, 0 } }, /* 297          MHz */
>> +	}
>> +};
>> +
>> +/**
>> + * hdmi_audio_get_coherent_n_cts() - compute N and CTS parameters for coherent
>> + * clocks. Coherent clock means that audio and TMDS clocks have the same
>> + * source (no drifts between clocks).
>> + *
>> + * @audio_fs: audio frame clock frequency in Hz
>> + * @tmds_clk: HDMI TMDS clock frequency in Hz
>> + * @n_cts: N and CTS parameter returned to user
>> + *
>> + * Values computed are based on table described in HDMI specification 1.4b
>> + *
>> + * Returns 0 on success or a negative error code on failure.
>> + */
>> +int hdmi_audio_get_coherent_n_cts(unsigned int audio_fs,
>> +				  unsigned int tmds_clk,
>> +				  struct hdmi_audio_n_cts *n_cts)
>> +{
>> +	int audio_freq_id, i;
>> +	int rate_coeff = 1;
>> +	u64 val, min;
>> +	const struct hdmi_audio_acr  *acr_table;
>> +	const struct hdmi_audio_n_cts *predef_n_cts = NULL;
>> +
>> +	switch (audio_fs) {
>> +	case 32000:
>> +		audio_freq_id = HDMI_AUDIO_N_CTS_32KHZ;
>> +		n_cts->n = 4096;
>> +		break;
>> +	case 44100:
>> +		audio_freq_id = HDMI_AUDIO_N_CTS_44_1KHZ;
>> +		n_cts->n = 6272;
>> +		break;
>> +	case 48000:
>> +		audio_freq_id = HDMI_AUDIO_N_CTS_48KHZ;
>> +		n_cts->n = 6144;
>> +		break;
>> +	case 88200:
>> +		audio_freq_id = HDMI_AUDIO_N_CTS_44_1KHZ;
>> +		rate_coeff = 2;
>> +		n_cts->n = 6272 * 2;
>> +		break;
>> +	case 96000:
>> +		audio_freq_id = HDMI_AUDIO_N_CTS_48KHZ;
>> +		rate_coeff = 2;
>> +		n_cts->n = 6144 * 2;
>> +		break;
>> +	case 176400:
>> +		audio_freq_id = HDMI_AUDIO_N_CTS_44_1KHZ;
>> +		rate_coeff = 4;
>> +		n_cts->n = 6272 * 4;
>> +		break;
>> +	case 192000:
>> +		audio_freq_id = HDMI_AUDIO_N_CTS_48KHZ;
>> +		rate_coeff = 4;
>> +		n_cts->n = 6144 * 4;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	acr_table = hdmi_audio_standard_acr[audio_freq_id];
>> +	for (i = 0; i < ARRAY_SIZE(hdmi_audio_standard_acr[0]); i++) {
>> +		if (tmds_clk == acr_table[i].tmds_clk) {
>> +			predef_n_cts = &acr_table[i].n_cts;
>> +			n_cts->n = predef_n_cts->n * rate_coeff;
>> +			n_cts->cts = predef_n_cts->cts;
>> +			n_cts->cts_1_ratio = predef_n_cts->cts_1_ratio;
>> +			return 0;
>> +		}
>> +	}
>> +
>> +	/*
>> +	 * 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);
>> +	}
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(hdmi_audio_get_coherent_n_cts);
>> +
>> +/**
>> + * hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent
>> + * clocks. None-coherent clocks means that audio and TMDS clocks have not the
>> + * same source (drifts between clocks). In this case assumption is that CTS is
>> + * automatically calculated by hardware.
>> + *
>> + * @audio_fs: audio frame clock frequency in Hz
>> + *
>> + * Values computed are based on table described in HDMI specification 1.4b
>> + *
>> + * Returns n value.
>> + */
>> +int hdmi_audio_get_non_coherent_n(unsigned int audio_fs)
>> +{
>> +	unsigned int n;
>> +
>> +	switch (audio_fs) {
>> +	case 32000:
>> +		n = 4096;
>> +		break;
>> +	case 44100:
>> +		n = 6272;
>> +		break;
>> +	case 48000:
>> +		n = 6144;
>> +		break;
>> +	case 88200:
>> +		n = 6272 * 2;
>> +		break;
>> +	case 96000:
>> +		n = 6144 * 2;
>> +		break;
>> +	case 176400:
>> +		n = 6272 * 4;
>> +		break;
>> +	case 192000:
>> +		n = 6144 * 4;
>> +		break;
>> +	default:
>> +		/* Not pre-defined, recommended value: 128 * fs / 1000 */
>> +		n = (audio_fs * 128) / 1000;
>> +	}
>> +
>> +	return n;
>> +}
>> +EXPORT_SYMBOL(hdmi_audio_get_non_coherent_n);
>> +
>> diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
>> index e974420..088d09f8 100644
>> --- a/include/linux/hdmi.h
>> +++ b/include/linux/hdmi.h
>> @@ -333,4 +333,28 @@ int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer);
>>  void hdmi_infoframe_log(const char *level, struct device *dev,
>>  			union hdmi_infoframe *frame);
>>  
>> +/**
>> + * struct hdmi_audio_n_cts - n and cts parameter for ACR packets
>> + * @n: N parameter
>> + * @cts: CTS parameter
>> + * @cts_1_ratio: ratio from 0 to 99 to alternate "CTS" and "CTS + 1" values
>> + *  ratio = 0: CTS parameter is accurate, no need to alternate with "CTS + 1"
>> + *             value
>> + *  ratio = x: Need to alternate with ACR "CTS + 1" value x percent of the time
>> + *             to generate accurate audio clock
>> + *  as exemple: if cts_1_ratio = 30: to have an accurate value, user
>> + *  should transfer CTS value 70% of the time and (CTS+1) value 30% of the time
>> + */
>> +struct hdmi_audio_n_cts {
>> +	unsigned int n;
>> +	unsigned int cts;
>> +	unsigned int cts_1_ratio;
>> +};
>> +
>> +int hdmi_audio_get_coherent_n_cts(unsigned int audio_fs,
>> +				  unsigned int tmds_clk,
>> +				  struct hdmi_audio_n_cts *n_cts);
>> +
>> +int hdmi_audio_get_non_coherent_n(unsigned int audio_fs);
>> +
>>  #endif /* _DRM_HDMI_H */
>>
Hi,

On Thu, Apr 21, 2016 at 8:29 AM, Arnaud Pouliquen <dianders@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?


> +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
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.

On 06/06/2016 06:34 PM, Doug Anderson wrote:
> Hi,
> 
> On Thu, Apr 21, 2016 at 8:29 AM, Arnaud Pouliquen <dianders@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 :-)

> 
> 
>> +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;
> }
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...
> 
> 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.
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.

Regards,
Arnaud

> 
> 
> -Doug
>
Arnaud,

On Tue, Jun 7, 2016 at 1:41 AM, Arnaud Pouliquen
<arnaud.pouliquen@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@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