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

drm: add drm_edid_to_eld helper extracting SADs from EDID (v2)

Some devices (ATI/AMD cards) don't support passing ELD struct to the
hardware but just require filling specific registers and then the
hardware/firmware does the rest. In such cases we need to read the info
from SAD blocks and put them in the correct registers.

agd5f: note that the returned pointer needs to be kfreed as per
Christian's suggestion.

v2: fix warning

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Reviewed-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Rafał Miłecki and committed by
Alex Deucher
fe214163 205996c0

+68
+59
drivers/gpu/drm/drm_edid.c
··· 2511 2511 EXPORT_SYMBOL(drm_edid_to_eld); 2512 2512 2513 2513 /** 2514 + * drm_edid_to_sad - extracts SADs from EDID 2515 + * @edid: EDID to parse 2516 + * @sads: pointer that will be set to the extracted SADs 2517 + * 2518 + * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it. 2519 + * Note: returned pointer needs to be kfreed 2520 + * 2521 + * Return number of found SADs or negative number on error. 2522 + */ 2523 + int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) 2524 + { 2525 + int count = 0; 2526 + int i, start, end, dbl; 2527 + u8 *cea; 2528 + 2529 + cea = drm_find_cea_extension(edid); 2530 + if (!cea) { 2531 + DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); 2532 + return -ENOENT; 2533 + } 2534 + 2535 + if (cea_revision(cea) < 3) { 2536 + DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); 2537 + return -ENOTSUPP; 2538 + } 2539 + 2540 + if (cea_db_offsets(cea, &start, &end)) { 2541 + DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); 2542 + return -EPROTO; 2543 + } 2544 + 2545 + for_each_cea_db(cea, i, start, end) { 2546 + u8 *db = &cea[i]; 2547 + 2548 + if (cea_db_tag(db) == AUDIO_BLOCK) { 2549 + int j; 2550 + dbl = cea_db_payload_len(db); 2551 + 2552 + count = dbl / 3; /* SAD is 3B */ 2553 + *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL); 2554 + if (!*sads) 2555 + return -ENOMEM; 2556 + for (j = 0; j < count; j++) { 2557 + u8 *sad = &db[1 + j * 3]; 2558 + 2559 + (*sads)[j].format = (sad[0] & 0x78) >> 3; 2560 + (*sads)[j].channels = sad[0] & 0x7; 2561 + (*sads)[j].freq = sad[1] & 0x7F; 2562 + (*sads)[j].byte2 = sad[2]; 2563 + } 2564 + break; 2565 + } 2566 + } 2567 + 2568 + return count; 2569 + } 2570 + EXPORT_SYMBOL(drm_edid_to_sad); 2571 + 2572 + /** 2514 2573 * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond 2515 2574 * @connector: connector associated with the HDMI/DP sink 2516 2575 * @mode: the display mode
+9
include/drm/drm_edid.h
··· 244 244 245 245 #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) 246 246 247 + /* Short Audio Descriptor */ 248 + struct cea_sad { 249 + u8 format; 250 + u8 channels; /* max number of channels - 1 */ 251 + u8 freq; 252 + u8 byte2; /* meaning depends on format */ 253 + }; 254 + 247 255 struct drm_encoder; 248 256 struct drm_connector; 249 257 struct drm_display_mode; 250 258 struct hdmi_avi_infoframe; 251 259 252 260 void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid); 261 + int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads); 253 262 int drm_av_sync_delay(struct drm_connector *connector, 254 263 struct drm_display_mode *mode); 255 264 struct drm_connector *drm_select_eld(struct drm_encoder *encoder,