return DIV_ROUND_CLOSEST_ULL(clock * bpc, 8);
 }
 EXPORT_SYMBOL(drm_hdmi_compute_mode_clock);
+
+struct drm_hdmi_acr_n_cts_entry {
+       unsigned int n;
+       unsigned int cts;
+};
+
+struct drm_hdmi_acr_data {
+       unsigned long tmds_clock_khz;
+       struct drm_hdmi_acr_n_cts_entry n_cts_32k,
+                                       n_cts_44k1,
+                                       n_cts_48k;
+};
+
+static const struct drm_hdmi_acr_data hdmi_acr_n_cts[] = {
+       {
+               /* "Other" entry */
+               .n_cts_32k =  { .n = 4096, },
+               .n_cts_44k1 = { .n = 6272, },
+               .n_cts_48k =  { .n = 6144, },
+       }, {
+               .tmds_clock_khz = 25175,
+               .n_cts_32k =  { .n = 4576,  .cts = 28125, },
+               .n_cts_44k1 = { .n = 7007,  .cts = 31250, },
+               .n_cts_48k =  { .n = 6864,  .cts = 28125, },
+       }, {
+               .tmds_clock_khz = 25200,
+               .n_cts_32k =  { .n = 4096,  .cts = 25200, },
+               .n_cts_44k1 = { .n = 6272,  .cts = 28000, },
+               .n_cts_48k =  { .n = 6144,  .cts = 25200, },
+       }, {
+               .tmds_clock_khz = 27000,
+               .n_cts_32k =  { .n = 4096,  .cts = 27000, },
+               .n_cts_44k1 = { .n = 6272,  .cts = 30000, },
+               .n_cts_48k =  { .n = 6144,  .cts = 27000, },
+       }, {
+               .tmds_clock_khz = 27027,
+               .n_cts_32k =  { .n = 4096,  .cts = 27027, },
+               .n_cts_44k1 = { .n = 6272,  .cts = 30030, },
+               .n_cts_48k =  { .n = 6144,  .cts = 27027, },
+       }, {
+               .tmds_clock_khz = 54000,
+               .n_cts_32k =  { .n = 4096,  .cts = 54000, },
+               .n_cts_44k1 = { .n = 6272,  .cts = 60000, },
+               .n_cts_48k =  { .n = 6144,  .cts = 54000, },
+       }, {
+               .tmds_clock_khz = 54054,
+               .n_cts_32k =  { .n = 4096,  .cts = 54054, },
+               .n_cts_44k1 = { .n = 6272,  .cts = 60060, },
+               .n_cts_48k =  { .n = 6144,  .cts = 54054, },
+       }, {
+               .tmds_clock_khz = 74176,
+               .n_cts_32k =  { .n = 11648, .cts = 210937, }, /* and 210938 */
+               .n_cts_44k1 = { .n = 17836, .cts = 234375, },
+               .n_cts_48k =  { .n = 11648, .cts = 140625, },
+       }, {
+               .tmds_clock_khz = 74250,
+               .n_cts_32k =  { .n = 4096,  .cts = 74250, },
+               .n_cts_44k1 = { .n = 6272,  .cts = 82500, },
+               .n_cts_48k =  { .n = 6144,  .cts = 74250, },
+       }, {
+               .tmds_clock_khz = 148352,
+               .n_cts_32k =  { .n = 11648, .cts = 421875, },
+               .n_cts_44k1 = { .n = 8918,  .cts = 234375, },
+               .n_cts_48k =  { .n = 5824,  .cts = 140625, },
+       }, {
+               .tmds_clock_khz = 148500,
+               .n_cts_32k =  { .n = 4096,  .cts = 148500, },
+               .n_cts_44k1 = { .n = 6272,  .cts = 165000, },
+               .n_cts_48k =  { .n = 6144,  .cts = 148500, },
+       }, {
+               .tmds_clock_khz = 296703,
+               .n_cts_32k =  { .n = 5824,  .cts = 421875, },
+               .n_cts_44k1 = { .n = 4459,  .cts = 234375, },
+               .n_cts_48k =  { .n = 5824,  .cts = 281250, },
+       }, {
+               .tmds_clock_khz = 297000,
+               .n_cts_32k =  { .n = 3072,  .cts = 222750, },
+               .n_cts_44k1 = { .n = 4704,  .cts = 247500, },
+               .n_cts_48k =  { .n = 5120,  .cts = 247500, },
+       }, {
+               .tmds_clock_khz = 593407,
+               .n_cts_32k =  { .n = 5824,  .cts = 843750, },
+               .n_cts_44k1 = { .n = 8918,  .cts = 937500, },
+               .n_cts_48k =  { .n = 5824,  .cts = 562500, },
+       }, {
+               .tmds_clock_khz = 594000,
+               .n_cts_32k =  { .n = 3072,  .cts = 445500, },
+               .n_cts_44k1 = { .n = 9408,  .cts = 990000, },
+               .n_cts_48k =  { .n = 6144,  .cts = 594000, },
+       },
+};
+
+static int drm_hdmi_acr_find_tmds_entry(unsigned long tmds_clock_khz)
+{
+       int i;
+
+       /* skip the "other" entry */
+       for (i = 1; i < ARRAY_SIZE(hdmi_acr_n_cts); i++) {
+               if (hdmi_acr_n_cts[i].tmds_clock_khz == tmds_clock_khz)
+                       return i;
+       }
+
+       return 0;
+}
+
+/**
+ * drm_hdmi_acr_get_n_cts() - get N and CTS values for Audio Clock Regeneration
+ *
+ * @tmds_char_rate: TMDS clock (char rate) as used by the HDMI connector
+ * @sample_rate: audio sample rate
+ * @out_n: a pointer to write the N value
+ * @out_cts: a pointer to write the CTS value
+ *
+ * Get the N and CTS values (either by calculating them or by returning data
+ * from the tables. This follows the HDMI 1.4b Section 7.2 "Audio Sample Clock
+ * Capture and Regeneration".
+ *
+ * Note, @sample_rate corresponds to the Fs value, see sections 7.2.4 - 7.2.6
+ * on how to select Fs for non-L-PCM formats.
+ */
+void
+drm_hdmi_acr_get_n_cts(unsigned long long tmds_char_rate,
+                      unsigned int sample_rate,
+                      unsigned int *out_n,
+                      unsigned int *out_cts)
+{
+       /* be a bit more tolerant, especially for the 1.001 entries */
+       unsigned long tmds_clock_khz = DIV_ROUND_CLOSEST_ULL(tmds_char_rate, 1000);
+       const struct drm_hdmi_acr_n_cts_entry *entry;
+       unsigned int n, cts, mult;
+       int tmds_idx;
+
+       tmds_idx = drm_hdmi_acr_find_tmds_entry(tmds_clock_khz);
+
+       /*
+        * Don't change the order, 192 kHz is divisible by 48k and 32k, but it
+        * should use 48k entry.
+        */
+       if (sample_rate % 48000 == 0) {
+               entry = &hdmi_acr_n_cts[tmds_idx].n_cts_48k;
+               mult = sample_rate / 48000;
+       } else if (sample_rate % 44100 == 0) {
+               entry = &hdmi_acr_n_cts[tmds_idx].n_cts_44k1;
+               mult = sample_rate / 44100;
+       } else if (sample_rate % 32000 == 0) {
+               entry = &hdmi_acr_n_cts[tmds_idx].n_cts_32k;
+               mult = sample_rate / 32000;
+       } else {
+               entry = NULL;
+       }
+
+       if (entry) {
+               n = entry->n * mult;
+               cts = entry->cts;
+       } else {
+               /* Recommended optimal value, HDMI 1.4b, Section 7.2.1 */
+               n = 128 * sample_rate / 1000;
+               cts = 0;
+       }
+
+       if (!cts)
+               cts = DIV_ROUND_CLOSEST_ULL(tmds_char_rate * n,
+                                           128 * sample_rate);
+
+       *out_n = n;
+       *out_cts = cts;
+}
+EXPORT_SYMBOL(drm_hdmi_acr_get_n_cts);