Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/display: hdmi: provide central data authority for ACR params

HDMI standard defines recommended N and CTS values for Audio Clock
Regeneration. Currently each driver implements those, frequently in
somewhat unique way. Provide a generic helper for getting those values
to be used by the HDMI drivers.

The helper is added to drm_hdmi_helper.c rather than drm_hdmi_audio.c
since HDMI drivers can be using this helper function even without
switching to DRM HDMI Audio helpers.

Note: currently this only handles the values per HDMI 1.4b Section 7.2
and HDMI 2.0 Section 9.2.1. Later the table can be expanded to
accommodate for Deep Color TMDS char rates per HDMI 1.4 Appendix D
and/or HDMI 2.0 / 2.1 Appendix C).

Reviewed-by: Maxime Ripard <mripard@kernel.org>
Link: https://lore.kernel.org/r/20250408-drm-hdmi-acr-v2-1-dee7298ab1af@oss.qualcomm.com
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>

+174
+168
drivers/gpu/drm/display/drm_hdmi_helper.c
··· 256 256 return DIV_ROUND_CLOSEST_ULL(clock * bpc, 8); 257 257 } 258 258 EXPORT_SYMBOL(drm_hdmi_compute_mode_clock); 259 + 260 + struct drm_hdmi_acr_n_cts_entry { 261 + unsigned int n; 262 + unsigned int cts; 263 + }; 264 + 265 + struct drm_hdmi_acr_data { 266 + unsigned long tmds_clock_khz; 267 + struct drm_hdmi_acr_n_cts_entry n_cts_32k, 268 + n_cts_44k1, 269 + n_cts_48k; 270 + }; 271 + 272 + static const struct drm_hdmi_acr_data hdmi_acr_n_cts[] = { 273 + { 274 + /* "Other" entry */ 275 + .n_cts_32k = { .n = 4096, }, 276 + .n_cts_44k1 = { .n = 6272, }, 277 + .n_cts_48k = { .n = 6144, }, 278 + }, { 279 + .tmds_clock_khz = 25175, 280 + .n_cts_32k = { .n = 4576, .cts = 28125, }, 281 + .n_cts_44k1 = { .n = 7007, .cts = 31250, }, 282 + .n_cts_48k = { .n = 6864, .cts = 28125, }, 283 + }, { 284 + .tmds_clock_khz = 25200, 285 + .n_cts_32k = { .n = 4096, .cts = 25200, }, 286 + .n_cts_44k1 = { .n = 6272, .cts = 28000, }, 287 + .n_cts_48k = { .n = 6144, .cts = 25200, }, 288 + }, { 289 + .tmds_clock_khz = 27000, 290 + .n_cts_32k = { .n = 4096, .cts = 27000, }, 291 + .n_cts_44k1 = { .n = 6272, .cts = 30000, }, 292 + .n_cts_48k = { .n = 6144, .cts = 27000, }, 293 + }, { 294 + .tmds_clock_khz = 27027, 295 + .n_cts_32k = { .n = 4096, .cts = 27027, }, 296 + .n_cts_44k1 = { .n = 6272, .cts = 30030, }, 297 + .n_cts_48k = { .n = 6144, .cts = 27027, }, 298 + }, { 299 + .tmds_clock_khz = 54000, 300 + .n_cts_32k = { .n = 4096, .cts = 54000, }, 301 + .n_cts_44k1 = { .n = 6272, .cts = 60000, }, 302 + .n_cts_48k = { .n = 6144, .cts = 54000, }, 303 + }, { 304 + .tmds_clock_khz = 54054, 305 + .n_cts_32k = { .n = 4096, .cts = 54054, }, 306 + .n_cts_44k1 = { .n = 6272, .cts = 60060, }, 307 + .n_cts_48k = { .n = 6144, .cts = 54054, }, 308 + }, { 309 + .tmds_clock_khz = 74176, 310 + .n_cts_32k = { .n = 11648, .cts = 210937, }, /* and 210938 */ 311 + .n_cts_44k1 = { .n = 17836, .cts = 234375, }, 312 + .n_cts_48k = { .n = 11648, .cts = 140625, }, 313 + }, { 314 + .tmds_clock_khz = 74250, 315 + .n_cts_32k = { .n = 4096, .cts = 74250, }, 316 + .n_cts_44k1 = { .n = 6272, .cts = 82500, }, 317 + .n_cts_48k = { .n = 6144, .cts = 74250, }, 318 + }, { 319 + .tmds_clock_khz = 148352, 320 + .n_cts_32k = { .n = 11648, .cts = 421875, }, 321 + .n_cts_44k1 = { .n = 8918, .cts = 234375, }, 322 + .n_cts_48k = { .n = 5824, .cts = 140625, }, 323 + }, { 324 + .tmds_clock_khz = 148500, 325 + .n_cts_32k = { .n = 4096, .cts = 148500, }, 326 + .n_cts_44k1 = { .n = 6272, .cts = 165000, }, 327 + .n_cts_48k = { .n = 6144, .cts = 148500, }, 328 + }, { 329 + .tmds_clock_khz = 296703, 330 + .n_cts_32k = { .n = 5824, .cts = 421875, }, 331 + .n_cts_44k1 = { .n = 4459, .cts = 234375, }, 332 + .n_cts_48k = { .n = 5824, .cts = 281250, }, 333 + }, { 334 + .tmds_clock_khz = 297000, 335 + .n_cts_32k = { .n = 3072, .cts = 222750, }, 336 + .n_cts_44k1 = { .n = 4704, .cts = 247500, }, 337 + .n_cts_48k = { .n = 5120, .cts = 247500, }, 338 + }, { 339 + .tmds_clock_khz = 593407, 340 + .n_cts_32k = { .n = 5824, .cts = 843750, }, 341 + .n_cts_44k1 = { .n = 8918, .cts = 937500, }, 342 + .n_cts_48k = { .n = 5824, .cts = 562500, }, 343 + }, { 344 + .tmds_clock_khz = 594000, 345 + .n_cts_32k = { .n = 3072, .cts = 445500, }, 346 + .n_cts_44k1 = { .n = 9408, .cts = 990000, }, 347 + .n_cts_48k = { .n = 6144, .cts = 594000, }, 348 + }, 349 + }; 350 + 351 + static int drm_hdmi_acr_find_tmds_entry(unsigned long tmds_clock_khz) 352 + { 353 + int i; 354 + 355 + /* skip the "other" entry */ 356 + for (i = 1; i < ARRAY_SIZE(hdmi_acr_n_cts); i++) { 357 + if (hdmi_acr_n_cts[i].tmds_clock_khz == tmds_clock_khz) 358 + return i; 359 + } 360 + 361 + return 0; 362 + } 363 + 364 + /** 365 + * drm_hdmi_acr_get_n_cts() - get N and CTS values for Audio Clock Regeneration 366 + * 367 + * @tmds_char_rate: TMDS clock (char rate) as used by the HDMI connector 368 + * @sample_rate: audio sample rate 369 + * @out_n: a pointer to write the N value 370 + * @out_cts: a pointer to write the CTS value 371 + * 372 + * Get the N and CTS values (either by calculating them or by returning data 373 + * from the tables. This follows the HDMI 1.4b Section 7.2 "Audio Sample Clock 374 + * Capture and Regeneration". 375 + * 376 + * Note, @sample_rate corresponds to the Fs value, see sections 7.2.4 - 7.2.6 377 + * on how to select Fs for non-L-PCM formats. 378 + */ 379 + void 380 + drm_hdmi_acr_get_n_cts(unsigned long long tmds_char_rate, 381 + unsigned int sample_rate, 382 + unsigned int *out_n, 383 + unsigned int *out_cts) 384 + { 385 + /* be a bit more tolerant, especially for the 1.001 entries */ 386 + unsigned long tmds_clock_khz = DIV_ROUND_CLOSEST_ULL(tmds_char_rate, 1000); 387 + const struct drm_hdmi_acr_n_cts_entry *entry; 388 + unsigned int n, cts, mult; 389 + int tmds_idx; 390 + 391 + tmds_idx = drm_hdmi_acr_find_tmds_entry(tmds_clock_khz); 392 + 393 + /* 394 + * Don't change the order, 192 kHz is divisible by 48k and 32k, but it 395 + * should use 48k entry. 396 + */ 397 + if (sample_rate % 48000 == 0) { 398 + entry = &hdmi_acr_n_cts[tmds_idx].n_cts_48k; 399 + mult = sample_rate / 48000; 400 + } else if (sample_rate % 44100 == 0) { 401 + entry = &hdmi_acr_n_cts[tmds_idx].n_cts_44k1; 402 + mult = sample_rate / 44100; 403 + } else if (sample_rate % 32000 == 0) { 404 + entry = &hdmi_acr_n_cts[tmds_idx].n_cts_32k; 405 + mult = sample_rate / 32000; 406 + } else { 407 + entry = NULL; 408 + } 409 + 410 + if (entry) { 411 + n = entry->n * mult; 412 + cts = entry->cts; 413 + } else { 414 + /* Recommended optimal value, HDMI 1.4b, Section 7.2.1 */ 415 + n = 128 * sample_rate / 1000; 416 + cts = 0; 417 + } 418 + 419 + if (!cts) 420 + cts = DIV_ROUND_CLOSEST_ULL(tmds_char_rate * n, 421 + 128 * sample_rate); 422 + 423 + *out_n = n; 424 + *out_cts = cts; 425 + } 426 + EXPORT_SYMBOL(drm_hdmi_acr_get_n_cts);
+6
include/drm/display/drm_hdmi_helper.h
··· 28 28 drm_hdmi_compute_mode_clock(const struct drm_display_mode *mode, 29 29 unsigned int bpc, enum hdmi_colorspace fmt); 30 30 31 + void 32 + drm_hdmi_acr_get_n_cts(unsigned long long tmds_char_rate, 33 + unsigned int sample_rate, 34 + unsigned int *out_n, 35 + unsigned int *out_cts); 36 + 31 37 #endif