From f53fe8ab3cd61b185e5eb3ba1af174175fc78725 Mon Sep 17 00:00:00 2001 From: Dom Cobley Date: Fri, 15 Sep 2023 17:25:17 +0100 Subject: [PATCH] drm/edid: Add option to report basic audio support with a generic edid Hardware that fails to read the edid is quite common. The kernel provides a mechanism to use one a of a few pre-built generic edid files for getting a basic video mode. However there is no easy was to add basic audio support, which requires a CTA extension block to report capabilities. Add an module option to request this. Signed-off-by: Dom Cobley --- drivers/gpu/drm/drm_edid.c | 17 +++++++++++++++++ drivers/gpu/drm/drm_edid_load.c | 34 +++++++++++++++++++++++++++++++++ include/drm/drm_edid.h | 2 ++ 3 files changed, 53 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 739e0d40cca619..419110878412a9 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2090,6 +2090,23 @@ static struct edid *edid_filter_invalid_blocks(struct edid *edid, return new; } +/* + * add a CTA extension (block) containing audio support + * fix up the base extension to include the extra + * extension and report as digital (required for audio) + * and fix up checksums. + */ +void drm_edid_add_audio_extension(void *block) +{ + struct edid *edid = block; + + edid->input = DRM_EDID_INPUT_DIGITAL; + edid->extensions++; + edid->checksum = edid_block_compute_checksum(edid); + edid = block + EDID_LENGTH; + edid->checksum = edid_block_compute_checksum(edid); +} + #define DDC_SEGMENT_ADDR 0x30 /** * drm_do_probe_ddc_edid() - get EDID information via I2C diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 37d8ba3ddb46af..4cedc674a93181 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -167,20 +167,53 @@ static int edid_size(const u8 *edid, int data_size) return (edid[0x7e] + 1) * EDID_LENGTH; } +/* Minimal edid extension block that reports basic audio support */ +static const u8 generic_edid_audio[] = { + 0x02, /* CTA extension block */ + 0x03, /* version */ + 0x12, /* 18 bytes are valid */ + 0xc0, /* underscan | basic audio */ + 0x23, /* Audio Data Block, length 3 */ + 0x09, /* Linear PCM, 2 channel */ + 0x07, /* Supported sample rates (kHz): 48 44.1 32 */ + 0x07, /* Supported sample sizes (bits): 24 20 16 */ + 0x83, /* Speaker Allocation Data Block, length 3 */ + 0x01, 0x00, 0x00, /* FL/FR */ + 0x65, /* Vendor-Specific Data Block, length 3 */ + 0x03, 0x0c, 0x00, 0x00, 0x00, /* HDMI PA:0.0.0.0 */ +}; + static void *edid_load(struct drm_connector *connector, const char *name, const char *connector_name) { const struct firmware *fw = NULL; const u8 *fwdata; u8 *edid; + u8 *fwdata2 = NULL; int fwsize, builtin; int i, valid_extensions = 0; bool print_bad_edid = !connector->bad_edid_counter || drm_debug_enabled(DRM_UT_KMS); + bool support_audio = false; + if (strncmp(name, "audio+", 6) == 0) { + support_audio = true; + name += 6; + } builtin = match_string(generic_edid_name, GENERIC_EDIDS, name); if (builtin >= 0) { fwdata = generic_edid[builtin]; fwsize = sizeof(generic_edid[builtin]); + if (support_audio) { + fwdata2 = kzalloc(fwsize + EDID_LENGTH, GFP_KERNEL); + if (!fwdata2) + return ERR_PTR(-ENOMEM); + + memcpy(fwdata2, fwdata, fwsize); + memcpy(fwdata2 + fwsize, generic_edid_audio, sizeof(generic_edid_audio)); + drm_edid_add_audio_extension(fwdata2); + fwsize += EDID_LENGTH; + fwdata = fwdata2; + } } else { struct platform_device *pdev; int err; @@ -260,6 +293,7 @@ static void *edid_load(struct drm_connector *connector, const char *name, out: release_firmware(fw); + kfree(fwdata2); return edid; } diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 1ed61e2b30a41c..72f33cd3c1d613 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -614,4 +614,6 @@ int drm_edid_connector_update(struct drm_connector *connector, const u8 *drm_find_edid_extension(const struct drm_edid *drm_edid, int ext_id, int *ext_index); +void drm_edid_add_audio_extension(void *block); + #endif /* __DRM_EDID_H__ */