diff options
Diffstat (limited to 'drivers/gpu/drm/display/drm_hdmi_state_helper.c')
-rw-r--r-- | drivers/gpu/drm/display/drm_hdmi_state_helper.c | 423 |
1 files changed, 381 insertions, 42 deletions
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index c205f37da1e12..a561f124be99a 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -1,15 +1,311 @@ // SPDX-License-Identifier: MIT +#include <linux/export.h> + #include <drm/drm_atomic.h> #include <drm/drm_connector.h> #include <drm/drm_edid.h> +#include <drm/drm_modes.h> #include <drm/drm_print.h> #include <drm/display/drm_hdmi_audio_helper.h> +#include <drm/display/drm_hdmi_cec_helper.h> #include <drm/display/drm_hdmi_helper.h> #include <drm/display/drm_hdmi_state_helper.h> /** + * DOC: hdmi helpers + * + * These functions contain an implementation of the HDMI specification + * in the form of KMS helpers. + * + * It contains TMDS character rate computation, automatic selection of + * output formats, infoframes generation, etc. + * + * Infoframes Compliance + * ~~~~~~~~~~~~~~~~~~~~~ + * + * Drivers using the helpers will expose the various infoframes + * generated according to the HDMI specification in debugfs. + * + * Compliance can then be tested using ``edid-decode`` from the ``v4l-utils`` project + * (https://git.linuxtv.org/v4l-utils.git/). A sample run would look like: + * + * .. code-block:: bash + * + * # edid-decode \ + * -I /sys/kernel/debug/dri/1/HDMI-A-1/infoframes/audio \ + * -I /sys/kernel/debug/dri/1/HDMI-A-1/infoframes/avi \ + * -I /sys/kernel/debug/dri/1/HDMI-A-1/infoframes/hdmi \ + * -I /sys/kernel/debug/dri/1/HDMI-A-1/infoframes/hdr_drm \ + * -I /sys/kernel/debug/dri/1/HDMI-A-1/infoframes/spd \ + * /sys/class/drm/card1-HDMI-A-1/edid \ + * -c + * + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 1e 6d f4 5b 1e ef 06 00 + * 07 20 01 03 80 2f 34 78 ea 24 05 af 4f 42 ab 25 + * 0f 50 54 21 08 00 d1 c0 61 40 45 40 01 01 01 01 + * 01 01 01 01 01 01 98 d0 00 40 a1 40 d4 b0 30 20 + * 3a 00 d1 0b 12 00 00 1a 00 00 00 fd 00 3b 3d 1e + * b2 31 00 0a 20 20 20 20 20 20 00 00 00 fc 00 4c + * 47 20 53 44 51 48 44 0a 20 20 20 20 00 00 00 ff + * 00 32 30 37 4e 54 52 4c 44 43 34 33 30 0a 01 46 + * + * 02 03 42 72 23 09 07 07 4d 01 03 04 90 12 13 1f + * 22 5d 5e 5f 60 61 83 01 00 00 6d 03 0c 00 10 00 + * b8 3c 20 00 60 01 02 03 67 d8 5d c4 01 78 80 03 + * e3 0f 00 18 e2 00 6a e3 05 c0 00 e6 06 05 01 52 + * 52 51 11 5d 00 a0 a0 40 29 b0 30 20 3a 00 d1 0b + * 12 00 00 1a 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c3 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: GSM + * Model: 23540 + * Serial Number: 454430 (0x0006ef1e) + * Made in: week 7 of 2022 + * Basic Display Parameters & Features: + * Digital display + * Maximum image size: 47 cm x 52 cm + * Gamma: 2.20 + * DPMS levels: Standby Suspend Off + * RGB color display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.6835, 0.3105 + * Green: 0.2587, 0.6679 + * Blue : 0.1445, 0.0585 + * White: 0.3134, 0.3291 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * DMT 0x09: 800x600 60.316541 Hz 4:3 37.879 kHz 40.000000 MHz + * DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz + * Standard Timings: + * DMT 0x52: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * DMT 0x10: 1024x768 60.003840 Hz 4:3 48.363 kHz 65.000000 MHz + * DMT 0x09: 800x600 60.316541 Hz 4:3 37.879 kHz 40.000000 MHz + * Detailed Timing Descriptors: + * DTD 1: 2560x2880 59.966580 Hz 8:9 185.417 kHz 534.000000 MHz (465 mm x 523 mm) + * Hfront 48 Hsync 32 Hback 240 Hpol P + * Vfront 3 Vsync 10 Vback 199 Vpol N + * Display Range Limits: + * Monitor ranges (GTF): 59-61 Hz V, 30-178 kHz H, max dotclock 490 MHz + * Display Product Name: 'LG SDQHD' + * Display Product Serial Number: '207NTRLDC430' + * Extension blocks: 1 + * Checksum: 0x46 + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Basic audio support + * Supports YCbCr 4:4:4 + * Supports YCbCr 4:2:2 + * Native detailed modes: 2 + * Audio Data Block: + * Linear PCM: + * Max channels: 2 + * Supported sample rates (kHz): 48 44.1 32 + * Supported sample sizes (bits): 24 20 16 + * Video Data Block: + * VIC 1: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * VIC 3: 720x480 59.940060 Hz 16:9 31.469 kHz 27.000000 MHz + * VIC 4: 1280x720 60.000000 Hz 16:9 45.000 kHz 74.250000 MHz + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (native) + * VIC 18: 720x576 50.000000 Hz 16:9 31.250 kHz 27.000000 MHz + * VIC 19: 1280x720 50.000000 Hz 16:9 37.500 kHz 74.250000 MHz + * VIC 31: 1920x1080 50.000000 Hz 16:9 56.250 kHz 148.500000 MHz + * VIC 34: 1920x1080 30.000000 Hz 16:9 33.750 kHz 74.250000 MHz + * VIC 93: 3840x2160 24.000000 Hz 16:9 54.000 kHz 297.000000 MHz + * VIC 94: 3840x2160 25.000000 Hz 16:9 56.250 kHz 297.000000 MHz + * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * VIC 96: 3840x2160 50.000000 Hz 16:9 112.500 kHz 594.000000 MHz + * VIC 97: 3840x2160 60.000000 Hz 16:9 135.000 kHz 594.000000 MHz + * Speaker Allocation Data Block: + * FL/FR - Front Left/Right + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.0.0.0 + * Supports_AI + * DC_36bit + * DC_30bit + * DC_Y444 + * Maximum TMDS clock: 300 MHz + * Extended HDMI video details: + * HDMI VICs: + * HDMI VIC 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * HDMI VIC 2: 3840x2160 25.000000 Hz 16:9 56.250 kHz 297.000000 MHz + * HDMI VIC 3: 3840x2160 24.000000 Hz 16:9 54.000 kHz 297.000000 MHz + * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: + * Version: 1 + * Maximum TMDS Character Rate: 600 MHz + * SCDC Present + * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding + * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding + * YCbCr 4:2:0 Capability Map Data Block: + * VIC 96: 3840x2160 50.000000 Hz 16:9 112.500 kHz 594.000000 MHz + * VIC 97: 3840x2160 60.000000 Hz 16:9 135.000 kHz 594.000000 MHz + * Video Capability Data Block: + * YCbCr quantization: No Data + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: Always Underscanned + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Colorimetry Data Block: + * BT2020YCC + * BT2020RGB + * HDR Static Metadata Data Block: + * Electro optical transfer functions: + * Traditional gamma - SDR luminance range + * SMPTE ST2084 + * Supported static metadata descriptors: + * Static metadata type 1 + * Desired content max luminance: 82 (295.365 cd/m^2) + * Desired content max frame-average luminance: 82 (295.365 cd/m^2) + * Desired content min luminance: 81 (0.298 cd/m^2) + * Detailed Timing Descriptors: + * DTD 2: 2560x2880 29.986961 Hz 8:9 87.592 kHz 238.250000 MHz (465 mm x 523 mm) + * Hfront 48 Hsync 32 Hback 80 Hpol P + * Vfront 3 Vsync 10 Vback 28 Vpol N + * Checksum: 0xc3 Unused space in Extension Block: 43 bytes + * + * ---------------- + * + * edid-decode 1.29.0-5346 + * edid-decode SHA: c363e9aa6d70 2025-03-11 11:41:18 + * + * Warnings: + * + * Block 1, CTA-861 Extension Block: + * IT Video Formats are overscanned by default, but normally this should be underscanned. + * Video Data Block: VIC 1 and the first DTD are not identical. Is this intended? + * Video Data Block: All VICs are in ascending order, and the first (preferred) VIC <= 4, is that intended? + * Video Capability Data Block: Set Selectable YCbCr Quantization to avoid interop issues. + * Video Capability Data Block: S_PT is equal to S_IT and S_CE, so should be set to 0 instead. + * Colorimetry Data Block: Set the sRGB colorimetry bit to avoid interop issues. + * Display Product Serial Number is set, so the Serial Number in the Base EDID should be 0. + * EDID: + * Base EDID: Some timings are out of range of the Monitor Ranges: + * Vertical Freq: 24.000 - 60.317 Hz (Monitor: 59.000 - 61.000 Hz) + * Horizontal Freq: 31.250 - 185.416 kHz (Monitor: 30.000 - 178.000 kHz) + * Maximum Clock: 594.000 MHz (Monitor: 490.000 MHz) + * + * Failures: + * + * Block 1, CTA-861 Extension Block: + * Video Capability Data Block: IT video formats are always underscanned, but bit 7 of Byte 3 of the CTA-861 Extension header is set to overscanned. + * EDID: + * CTA-861: Native progressive timings are a mix of several resolutions. + * + * EDID conformity: FAIL + * + * ================ + * + * InfoFrame of '/sys/kernel/debug/dri/1/HDMI-A-1/infoframes/audio' was empty. + * + * ================ + * + * edid-decode InfoFrame (hex): + * + * 82 02 0d 31 12 28 04 00 00 00 00 00 00 00 00 00 + * 00 + * + * ---------------- + * + * HDMI InfoFrame Checksum: 0x31 + * + * AVI InfoFrame + * Version: 2 + * Length: 13 + * Y: Color Component Sample Format: RGB + * A: Active Format Information Present: Yes + * B: Bar Data Present: Bar Data not present + * S: Scan Information: Composed for an underscanned display + * C: Colorimetry: No Data + * M: Picture Aspect Ratio: 16:9 + * R: Active Portion Aspect Ratio: 8 + * ITC: IT Content: No Data + * EC: Extended Colorimetry: xvYCC601 + * Q: RGB Quantization Range: Limited Range + * SC: Non-Uniform Picture Scaling: No Known non-uniform scaling + * YQ: YCC Quantization Range: Limited Range + * CN: IT Content Type: Graphics + * PR: Pixel Data Repetition Count: 0 + * Line Number of End of Top Bar: 0 + * Line Number of Start of Bottom Bar: 0 + * Pixel Number of End of Left Bar: 0 + * Pixel Number of Start of Right Bar: 0 + * + * ---------------- + * + * AVI InfoFrame conformity: PASS + * + * ================ + * + * edid-decode InfoFrame (hex): + * + * 81 01 05 49 03 0c 00 20 01 + * + * ---------------- + * + * HDMI InfoFrame Checksum: 0x49 + * + * Vendor-Specific InfoFrame (HDMI), OUI 00-0C-03 + * Version: 1 + * Length: 5 + * HDMI Video Format: HDMI_VIC is present + * HDMI VIC 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * + * ---------------- + * + * Vendor-Specific InfoFrame (HDMI), OUI 00-0C-03 conformity: PASS + * + * ================ + * + * InfoFrame of '/sys/kernel/debug/dri/1/HDMI-A-1/infoframes/hdr_drm' was empty. + * + * ================ + * + * edid-decode InfoFrame (hex): + * + * 83 01 19 93 42 72 6f 61 64 63 6f 6d 56 69 64 65 + * 6f 63 6f 72 65 00 00 00 00 00 00 00 09 + * + * ---------------- + * + * HDMI InfoFrame Checksum: 0x93 + * + * Source Product Description InfoFrame + * Version: 1 + * Length: 25 + * Vendor Name: 'Broadcom' + * Product Description: 'Videocore' + * Source Information: PC general + * + * ---------------- + * + * Source Product Description InfoFrame conformity: PASS + * + * Testing + * ~~~~~~~ + * + * The helpers have unit testing and can be tested using kunit with: + * + * .. code-block:: bash + * + * $ ./tools/testing/kunit/kunit.py run \ + * --kunitconfig=drivers/gpu/drm/tests \ + * drm_atomic_helper_connector_hdmi_* + */ + +/** * __drm_atomic_helper_connector_hdmi_reset() - Initializes all HDMI @drm_connector_state resources * @connector: DRM connector * @new_conn_state: connector state to reset @@ -115,6 +411,11 @@ sink_supports_format_bpc(const struct drm_connector *connector, return false; } + if (drm_mode_is_420_only(info, mode) && format != HDMI_COLORSPACE_YUV420) { + drm_dbg_kms(dev, "Mode can be only supported in YUV420 format.\n"); + return false; + } + switch (format) { case HDMI_COLORSPACE_RGB: drm_dbg_kms(dev, "RGB Format, checking the constraints.\n"); @@ -145,9 +446,36 @@ sink_supports_format_bpc(const struct drm_connector *connector, return true; case HDMI_COLORSPACE_YUV420: - /* TODO: YUV420 is unsupported at the moment. */ - drm_dbg_kms(dev, "YUV420 format isn't supported yet.\n"); - return false; + drm_dbg_kms(dev, "YUV420 format, checking the constraints.\n"); + + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR420)) { + drm_dbg_kms(dev, "Sink doesn't support YUV420.\n"); + return false; + } + + if (!drm_mode_is_420(info, mode)) { + drm_dbg_kms(dev, "Mode cannot be supported in YUV420 format.\n"); + return false; + } + + if (bpc == 10 && !(info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)) { + drm_dbg_kms(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); + return false; + } + + if (bpc == 12 && !(info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)) { + drm_dbg_kms(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); + return false; + } + + if (bpc == 16 && !(info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)) { + drm_dbg_kms(dev, "16 BPC but sink doesn't support Deep Color 48.\n"); + return false; + } + + drm_dbg_kms(dev, "YUV420 format supported in that configuration.\n"); + + return true; case HDMI_COLORSPACE_YUV422: drm_dbg_kms(dev, "YUV422 format, checking the constraints.\n"); @@ -253,8 +581,9 @@ hdmi_try_format_bpc(const struct drm_connector *connector, struct drm_device *dev = connector->dev; int ret; - drm_dbg_kms(dev, "Trying %s output format\n", - drm_hdmi_connector_get_output_format_name(fmt)); + drm_dbg_kms(dev, "Trying %s output format with %u bpc\n", + drm_hdmi_connector_get_output_format_name(fmt), + bpc); if (!sink_supports_format_bpc(connector, info, mode, fmt, bpc)) { drm_dbg_kms(dev, "%s output format not supported with %u bpc\n", @@ -271,7 +600,7 @@ hdmi_try_format_bpc(const struct drm_connector *connector, return false; } - drm_dbg_kms(dev, "%s output format supported with %u (TMDS char rate: %llu Hz)\n", + drm_dbg_kms(dev, "%s output format supported with %u bpc (TMDS char rate: %llu Hz)\n", drm_hdmi_connector_get_output_format_name(fmt), bpc, conn_state->hdmi.tmds_char_rate); @@ -279,23 +608,35 @@ hdmi_try_format_bpc(const struct drm_connector *connector, } static int -hdmi_compute_format(const struct drm_connector *connector, - struct drm_connector_state *conn_state, - const struct drm_display_mode *mode, - unsigned int bpc) +hdmi_compute_format_bpc(const struct drm_connector *connector, + struct drm_connector_state *conn_state, + const struct drm_display_mode *mode, + unsigned int max_bpc, enum hdmi_colorspace fmt) { struct drm_device *dev = connector->dev; + unsigned int bpc; + int ret; + + for (bpc = max_bpc; bpc >= 8; bpc -= 2) { + ret = hdmi_try_format_bpc(connector, conn_state, mode, bpc, fmt); + if (!ret) + continue; + + conn_state->hdmi.output_bpc = bpc; + conn_state->hdmi.output_format = fmt; + + drm_dbg_kms(dev, + "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n", + mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), + conn_state->hdmi.output_bpc, + drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format), + conn_state->hdmi.tmds_char_rate); - /* - * TODO: Add support for YCbCr420 output for HDMI 2.0 capable - * devices, for modes that only support YCbCr420. - */ - if (hdmi_try_format_bpc(connector, conn_state, mode, bpc, HDMI_COLORSPACE_RGB)) { - conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB; return 0; } - drm_dbg_kms(dev, "Failed. No Format Supported for that bpc count.\n"); + drm_dbg_kms(dev, "Failed. %s output format not supported for any bpc count.\n", + drm_hdmi_connector_get_output_format_name(fmt)); return -EINVAL; } @@ -305,33 +646,29 @@ hdmi_compute_config(const struct drm_connector *connector, struct drm_connector_state *conn_state, const struct drm_display_mode *mode) { - struct drm_device *dev = connector->dev; unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, connector->max_bpc); - unsigned int bpc; int ret; - for (bpc = max_bpc; bpc >= 8; bpc -= 2) { - drm_dbg_kms(dev, "Trying with a %d bpc output\n", bpc); - - ret = hdmi_compute_format(connector, conn_state, mode, bpc); - if (ret) - continue; - - conn_state->hdmi.output_bpc = bpc; - - drm_dbg_kms(dev, - "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n", - mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), - conn_state->hdmi.output_bpc, - drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format), - conn_state->hdmi.tmds_char_rate); - - return 0; + ret = hdmi_compute_format_bpc(connector, conn_state, mode, max_bpc, + HDMI_COLORSPACE_RGB); + if (ret) { + if (connector->ycbcr_420_allowed) { + ret = hdmi_compute_format_bpc(connector, conn_state, + mode, max_bpc, + HDMI_COLORSPACE_YUV420); + if (ret) + drm_dbg_kms(connector->dev, + "YUV420 output format doesn't work.\n"); + } else { + drm_dbg_kms(connector->dev, + "YUV420 output format not allowed for connector.\n"); + ret = -EINVAL; + } } - return -EINVAL; + return ret; } static int hdmi_generate_avi_infoframe(const struct drm_connector *connector, @@ -506,12 +843,12 @@ int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, if (!new_conn_state->crtc || !new_conn_state->best_encoder) return 0; - new_conn_state->hdmi.is_limited_range = hdmi_is_limited_range(connector, new_conn_state); - ret = hdmi_compute_config(connector, new_conn_state, mode); if (ret) return ret; + new_conn_state->hdmi.is_limited_range = hdmi_is_limited_range(connector, new_conn_state); + ret = hdmi_generate_infoframes(connector, new_conn_state); if (ret) return ret; @@ -789,9 +1126,10 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector, const struct drm_edid *drm_edid; if (status == connector_status_disconnected) { - // TODO: also handle CEC and scramber, HDMI sink disconnected. + // TODO: also handle scramber, HDMI sink disconnected. drm_connector_hdmi_audio_plugged_notify(connector, false); drm_edid_connector_update(connector, NULL); + drm_connector_cec_phys_addr_invalidate(connector); return; } @@ -805,8 +1143,9 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector, drm_edid_free(drm_edid); if (status == connector_status_connected) { - // TODO: also handle CEC and scramber, HDMI sink is now connected. + // TODO: also handle scramber, HDMI sink is now connected. drm_connector_hdmi_audio_plugged_notify(connector, true); + drm_connector_cec_phys_addr_set(connector); } } @@ -816,7 +1155,7 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector, * @status: Connection status * * This function should be called as a part of the .detect() / .detect_ctx() - * callbacks, updating the HDMI-specific connector's data. + * callbacks for all status changes. */ void drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector, enum drm_connector_status status) |