diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index fd033ebbdf84..c1a26217a530 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1267,7 +1267,35 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, } #define HDMI_IDENTIFIER 0x000C03 +#define AUDIO_BLOCK 0x01 #define VENDOR_BLOCK 0x03 +#define EDID_BASIC_AUDIO (1 << 6) + +/** + * Search EDID for CEA extension block. + */ +static u8 *drm_find_cea_extension(struct edid *edid) +{ + u8 *edid_ext = NULL; + int i; + + /* No EDID or EDID extensions */ + if (edid == NULL || edid->extensions == 0) + return NULL; + + /* Find CEA extension */ + for (i = 0; i < edid->extensions; i++) { + edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1); + if (edid_ext[0] == CEA_EXT) + break; + } + + if (i == edid->extensions) + return NULL; + + return edid_ext; +} + /** * drm_detect_hdmi_monitor - detect whether monitor is hdmi. * @edid: monitor EDID information @@ -1277,24 +1305,13 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, */ bool drm_detect_hdmi_monitor(struct edid *edid) { - char *edid_ext = NULL; + u8 *edid_ext; int i, hdmi_id; int start_offset, end_offset; bool is_hdmi = false; - /* No EDID or EDID extensions */ - if (edid == NULL || edid->extensions == 0) - goto end; - - /* Find CEA extension */ - for (i = 0; i < edid->extensions; i++) { - edid_ext = (char *)edid + EDID_LENGTH * (i + 1); - /* This block is CEA extension */ - if (edid_ext[0] == 0x02) - break; - } - - if (i == edid->extensions) + edid_ext = drm_find_cea_extension(edid); + if (!edid_ext) goto end; /* Data block offset in CEA extension block */ @@ -1324,6 +1341,53 @@ end: } EXPORT_SYMBOL(drm_detect_hdmi_monitor); +/** + * drm_detect_monitor_audio - check monitor audio capability + * + * Monitor should have CEA extension block. + * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic + * audio' only. If there is any audio extension block and supported + * audio format, assume at least 'basic audio' support, even if 'basic + * audio' is not defined in EDID. + * + */ +bool drm_detect_monitor_audio(struct edid *edid) +{ + u8 *edid_ext; + int i, j; + bool has_audio = false; + int start_offset, end_offset; + + edid_ext = drm_find_cea_extension(edid); + if (!edid_ext) + goto end; + + has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0); + + if (has_audio) { + DRM_DEBUG_KMS("Monitor has basic audio support\n"); + goto end; + } + + /* Data block offset in CEA extension block */ + start_offset = 4; + end_offset = edid_ext[2]; + + for (i = start_offset; i < end_offset; + i += ((edid_ext[i] & 0x1f) + 1)) { + if ((edid_ext[i] >> 5) == AUDIO_BLOCK) { + has_audio = true; + for (j = 1; j < (edid_ext[i] & 0x1f); j += 3) + DRM_DEBUG_KMS("CEA audio format %d\n", + (edid_ext[i + j] >> 3) & 0xf); + goto end; + } + } +end: + return has_audio; +} +EXPORT_SYMBOL(drm_detect_monitor_audio); + /** * drm_add_edid_modes - add modes from EDID data, if available * @connector: connector we're probing diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 15c4796fd467..029aa688e787 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -763,6 +763,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern bool drm_detect_hdmi_monitor(struct edid *edid); +extern bool drm_detect_monitor_audio(struct edid *edid); extern int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev,