mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-31 22:23:05 +00:00

Fix the compile-time warnings
drivers/gpu/drm/display/drm_bridge_connector.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_dp_aux_bus.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_dp_cec.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_dp_helper.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_dp_mst_topology.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_dp_tunnel.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_dsc_helper.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_hdmi_audio_helper.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_hdmi_cec_helper.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_hdmi_helper.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_hdmi_state_helper.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
drivers/gpu/drm/display/drm_scdc_helper.c: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: a934a57a42
("scripts/misc-check: check missing #include <linux/export.h> when W=1")
Reviewed-by: André Almeida <andrealmeid@igalia.com>
Cc: Masahiro Yamada <masahiroy@kernel.org>
Cc: Nathan Chancellor <nathan@kernel.org>
Link: https://lore.kernel.org/r/20250612121633.229222-6-tzimmermann@suse.de
1181 lines
35 KiB
C
1181 lines
35 KiB
C
// 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
|
|
*
|
|
* Initializes all HDMI resources from a @drm_connector_state without
|
|
* actually allocating it. This is useful for HDMI drivers, in
|
|
* combination with __drm_atomic_helper_connector_reset() or
|
|
* drm_atomic_helper_connector_reset().
|
|
*/
|
|
void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector,
|
|
struct drm_connector_state *new_conn_state)
|
|
{
|
|
unsigned int max_bpc = connector->max_bpc;
|
|
|
|
new_conn_state->max_bpc = max_bpc;
|
|
new_conn_state->max_requested_bpc = max_bpc;
|
|
new_conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
|
|
}
|
|
EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset);
|
|
|
|
static const struct drm_display_mode *
|
|
connector_state_get_mode(const struct drm_connector_state *conn_state)
|
|
{
|
|
struct drm_atomic_state *state;
|
|
struct drm_crtc_state *crtc_state;
|
|
struct drm_crtc *crtc;
|
|
|
|
state = conn_state->state;
|
|
if (!state)
|
|
return NULL;
|
|
|
|
crtc = conn_state->crtc;
|
|
if (!crtc)
|
|
return NULL;
|
|
|
|
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
|
if (!crtc_state)
|
|
return NULL;
|
|
|
|
return &crtc_state->mode;
|
|
}
|
|
|
|
static bool hdmi_is_limited_range(const struct drm_connector *connector,
|
|
const struct drm_connector_state *conn_state)
|
|
{
|
|
const struct drm_display_info *info = &connector->display_info;
|
|
const struct drm_display_mode *mode =
|
|
connector_state_get_mode(conn_state);
|
|
|
|
/*
|
|
* The Broadcast RGB property only applies to RGB format, and
|
|
* i915 just assumes limited range for YCbCr output, so let's
|
|
* just do the same.
|
|
*/
|
|
if (conn_state->hdmi.output_format != HDMI_COLORSPACE_RGB)
|
|
return true;
|
|
|
|
if (conn_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_FULL)
|
|
return false;
|
|
|
|
if (conn_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
|
|
return true;
|
|
|
|
if (!info->is_hdmi)
|
|
return false;
|
|
|
|
return drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
|
|
}
|
|
|
|
static bool
|
|
sink_supports_format_bpc(const struct drm_connector *connector,
|
|
const struct drm_display_info *info,
|
|
const struct drm_display_mode *mode,
|
|
unsigned int format, unsigned int bpc)
|
|
{
|
|
struct drm_device *dev = connector->dev;
|
|
u8 vic = drm_match_cea_mode(mode);
|
|
|
|
/*
|
|
* CTA-861-F, section 5.4 - Color Coding & Quantization states
|
|
* that the bpc must be 8, 10, 12 or 16 except for the default
|
|
* 640x480 VIC1 where the value must be 8.
|
|
*
|
|
* The definition of default here is ambiguous but the spec
|
|
* refers to VIC1 being the default timing in several occasions
|
|
* so our understanding is that for the default timing (ie,
|
|
* VIC1), the bpc must be 8.
|
|
*/
|
|
if (vic == 1 && bpc != 8) {
|
|
drm_dbg_kms(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
|
|
return false;
|
|
}
|
|
|
|
if (!info->is_hdmi &&
|
|
(format != HDMI_COLORSPACE_RGB || bpc != 8)) {
|
|
drm_dbg_kms(dev, "DVI Monitors require an RGB output at 8 bpc\n");
|
|
return false;
|
|
}
|
|
|
|
if (!(connector->hdmi.supported_formats & BIT(format))) {
|
|
drm_dbg_kms(dev, "%s format unsupported by the connector.\n",
|
|
drm_hdmi_connector_get_output_format_name(format));
|
|
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");
|
|
|
|
/*
|
|
* In some cases, like when the EDID readout fails, or
|
|
* is not an HDMI compliant EDID for some reason, the
|
|
* color_formats field will be blank and not report any
|
|
* format supported. In such a case, assume that RGB is
|
|
* supported so we can keep things going and light up
|
|
* the display.
|
|
*/
|
|
if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
|
|
drm_warn(dev, "HDMI Sink doesn't support RGB, something's wrong.\n");
|
|
|
|
if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) {
|
|
drm_dbg_kms(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
|
|
return false;
|
|
}
|
|
|
|
if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) {
|
|
drm_dbg_kms(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
|
|
return false;
|
|
}
|
|
|
|
drm_dbg_kms(dev, "RGB format supported in that configuration.\n");
|
|
|
|
return true;
|
|
|
|
case HDMI_COLORSPACE_YUV420:
|
|
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");
|
|
|
|
if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
|
|
drm_dbg_kms(dev, "Sink doesn't support YUV422.\n");
|
|
return false;
|
|
}
|
|
|
|
if (bpc > 12) {
|
|
drm_dbg_kms(dev, "YUV422 only supports 12 bpc or lower.\n");
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* HDMI Spec 1.3 - Section 6.5 Pixel Encodings and Color Depth
|
|
* states that Deep Color is not relevant for YUV422 so we
|
|
* don't need to check the Deep Color bits in the EDIDs here.
|
|
*/
|
|
|
|
drm_dbg_kms(dev, "YUV422 format supported in that configuration.\n");
|
|
|
|
return true;
|
|
|
|
case HDMI_COLORSPACE_YUV444:
|
|
drm_dbg_kms(dev, "YUV444 format, checking the constraints.\n");
|
|
|
|
if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) {
|
|
drm_dbg_kms(dev, "Sink doesn't support YUV444.\n");
|
|
return false;
|
|
}
|
|
|
|
if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) {
|
|
drm_dbg_kms(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
|
|
return false;
|
|
}
|
|
|
|
if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) {
|
|
drm_dbg_kms(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
|
|
return false;
|
|
}
|
|
|
|
drm_dbg_kms(dev, "YUV444 format supported in that configuration.\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
drm_dbg_kms(dev, "Unsupported pixel format.\n");
|
|
return false;
|
|
}
|
|
|
|
static enum drm_mode_status
|
|
hdmi_clock_valid(const struct drm_connector *connector,
|
|
const struct drm_display_mode *mode,
|
|
unsigned long long clock)
|
|
{
|
|
const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
|
|
const struct drm_display_info *info = &connector->display_info;
|
|
|
|
if (info->max_tmds_clock && clock > info->max_tmds_clock * 1000)
|
|
return MODE_CLOCK_HIGH;
|
|
|
|
if (funcs && funcs->tmds_char_rate_valid) {
|
|
enum drm_mode_status status;
|
|
|
|
status = funcs->tmds_char_rate_valid(connector, mode, clock);
|
|
if (status != MODE_OK)
|
|
return status;
|
|
}
|
|
|
|
return MODE_OK;
|
|
}
|
|
|
|
static int
|
|
hdmi_compute_clock(const struct drm_connector *connector,
|
|
struct drm_connector_state *conn_state,
|
|
const struct drm_display_mode *mode,
|
|
unsigned int bpc, enum hdmi_colorspace fmt)
|
|
{
|
|
enum drm_mode_status status;
|
|
unsigned long long clock;
|
|
|
|
clock = drm_hdmi_compute_mode_clock(mode, bpc, fmt);
|
|
if (!clock)
|
|
return -EINVAL;
|
|
|
|
status = hdmi_clock_valid(connector, mode, clock);
|
|
if (status != MODE_OK)
|
|
return -EINVAL;
|
|
|
|
conn_state->hdmi.tmds_char_rate = clock;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
hdmi_try_format_bpc(const struct drm_connector *connector,
|
|
struct drm_connector_state *conn_state,
|
|
const struct drm_display_mode *mode,
|
|
unsigned int bpc, enum hdmi_colorspace fmt)
|
|
{
|
|
const struct drm_display_info *info = &connector->display_info;
|
|
struct drm_device *dev = connector->dev;
|
|
int ret;
|
|
|
|
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",
|
|
drm_hdmi_connector_get_output_format_name(fmt),
|
|
bpc);
|
|
return false;
|
|
}
|
|
|
|
ret = hdmi_compute_clock(connector, conn_state, mode, bpc, fmt);
|
|
if (ret) {
|
|
drm_dbg_kms(dev, "Couldn't compute clock for %s output format and %u bpc\n",
|
|
drm_hdmi_connector_get_output_format_name(fmt),
|
|
bpc);
|
|
return false;
|
|
}
|
|
|
|
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);
|
|
|
|
return true;
|
|
}
|
|
|
|
static int
|
|
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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int
|
|
hdmi_compute_config(const struct drm_connector *connector,
|
|
struct drm_connector_state *conn_state,
|
|
const struct drm_display_mode *mode)
|
|
{
|
|
unsigned int max_bpc = clamp_t(unsigned int,
|
|
conn_state->max_bpc,
|
|
8, connector->max_bpc);
|
|
int ret;
|
|
|
|
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 ret;
|
|
}
|
|
|
|
static int hdmi_generate_avi_infoframe(const struct drm_connector *connector,
|
|
struct drm_connector_state *conn_state)
|
|
{
|
|
const struct drm_display_mode *mode =
|
|
connector_state_get_mode(conn_state);
|
|
struct drm_connector_hdmi_infoframe *infoframe =
|
|
&conn_state->hdmi.infoframes.avi;
|
|
struct hdmi_avi_infoframe *frame =
|
|
&infoframe->data.avi;
|
|
bool is_limited_range = conn_state->hdmi.is_limited_range;
|
|
enum hdmi_quantization_range rgb_quant_range =
|
|
is_limited_range ? HDMI_QUANTIZATION_RANGE_LIMITED : HDMI_QUANTIZATION_RANGE_FULL;
|
|
int ret;
|
|
|
|
infoframe->set = false;
|
|
|
|
ret = drm_hdmi_avi_infoframe_from_display_mode(frame, connector, mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
frame->colorspace = conn_state->hdmi.output_format;
|
|
|
|
/*
|
|
* FIXME: drm_hdmi_avi_infoframe_quant_range() doesn't handle
|
|
* YUV formats at all at the moment, so if we ever support YUV
|
|
* formats this needs to be revised.
|
|
*/
|
|
drm_hdmi_avi_infoframe_quant_range(frame, connector, mode, rgb_quant_range);
|
|
drm_hdmi_avi_infoframe_colorimetry(frame, conn_state);
|
|
drm_hdmi_avi_infoframe_bars(frame, conn_state);
|
|
|
|
infoframe->set = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hdmi_generate_spd_infoframe(const struct drm_connector *connector,
|
|
struct drm_connector_state *conn_state)
|
|
{
|
|
struct drm_connector_hdmi_infoframe *infoframe =
|
|
&conn_state->hdmi.infoframes.spd;
|
|
struct hdmi_spd_infoframe *frame =
|
|
&infoframe->data.spd;
|
|
int ret;
|
|
|
|
infoframe->set = false;
|
|
|
|
ret = hdmi_spd_infoframe_init(frame,
|
|
connector->hdmi.vendor,
|
|
connector->hdmi.product);
|
|
if (ret)
|
|
return ret;
|
|
|
|
frame->sdi = HDMI_SPD_SDI_PC;
|
|
|
|
infoframe->set = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hdmi_generate_hdr_infoframe(const struct drm_connector *connector,
|
|
struct drm_connector_state *conn_state)
|
|
{
|
|
struct drm_connector_hdmi_infoframe *infoframe =
|
|
&conn_state->hdmi.infoframes.hdr_drm;
|
|
struct hdmi_drm_infoframe *frame =
|
|
&infoframe->data.drm;
|
|
int ret;
|
|
|
|
infoframe->set = false;
|
|
|
|
if (connector->max_bpc < 10)
|
|
return 0;
|
|
|
|
if (!conn_state->hdr_output_metadata)
|
|
return 0;
|
|
|
|
ret = drm_hdmi_infoframe_set_hdr_metadata(frame, conn_state);
|
|
if (ret)
|
|
return ret;
|
|
|
|
infoframe->set = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hdmi_generate_hdmi_vendor_infoframe(const struct drm_connector *connector,
|
|
struct drm_connector_state *conn_state)
|
|
{
|
|
const struct drm_display_info *info = &connector->display_info;
|
|
const struct drm_display_mode *mode =
|
|
connector_state_get_mode(conn_state);
|
|
struct drm_connector_hdmi_infoframe *infoframe =
|
|
&conn_state->hdmi.infoframes.hdmi;
|
|
struct hdmi_vendor_infoframe *frame =
|
|
&infoframe->data.vendor.hdmi;
|
|
int ret;
|
|
|
|
infoframe->set = false;
|
|
|
|
if (!info->has_hdmi_infoframe)
|
|
return 0;
|
|
|
|
ret = drm_hdmi_vendor_infoframe_from_display_mode(frame, connector, mode);
|
|
if (ret)
|
|
return ret;
|
|
|
|
infoframe->set = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hdmi_generate_infoframes(const struct drm_connector *connector,
|
|
struct drm_connector_state *conn_state)
|
|
{
|
|
const struct drm_display_info *info = &connector->display_info;
|
|
int ret;
|
|
|
|
if (!info->is_hdmi)
|
|
return 0;
|
|
|
|
ret = hdmi_generate_avi_infoframe(connector, conn_state);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = hdmi_generate_spd_infoframe(connector, conn_state);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* Audio Infoframes will be generated by ALSA, and updated by
|
|
* drm_atomic_helper_connector_hdmi_update_audio_infoframe().
|
|
*/
|
|
|
|
ret = hdmi_generate_hdr_infoframe(connector, conn_state);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = hdmi_generate_hdmi_vendor_infoframe(connector, conn_state);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state
|
|
* @connector: DRM Connector
|
|
* @state: the DRM State object
|
|
*
|
|
* Provides a default connector state check handler for HDMI connectors.
|
|
* Checks that a desired connector update is valid, and updates various
|
|
* fields of derived state.
|
|
*
|
|
* RETURNS:
|
|
* Zero on success, or an errno code otherwise.
|
|
*/
|
|
int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
struct drm_connector_state *old_conn_state =
|
|
drm_atomic_get_old_connector_state(state, connector);
|
|
struct drm_connector_state *new_conn_state =
|
|
drm_atomic_get_new_connector_state(state, connector);
|
|
const struct drm_display_mode *mode =
|
|
connector_state_get_mode(new_conn_state);
|
|
int ret;
|
|
|
|
if (!new_conn_state->crtc || !new_conn_state->best_encoder)
|
|
return 0;
|
|
|
|
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;
|
|
|
|
if (old_conn_state->hdmi.broadcast_rgb != new_conn_state->hdmi.broadcast_rgb ||
|
|
old_conn_state->hdmi.output_bpc != new_conn_state->hdmi.output_bpc ||
|
|
old_conn_state->hdmi.output_format != new_conn_state->hdmi.output_format) {
|
|
struct drm_crtc *crtc = new_conn_state->crtc;
|
|
struct drm_crtc_state *crtc_state;
|
|
|
|
crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
|
if (IS_ERR(crtc_state))
|
|
return PTR_ERR(crtc_state);
|
|
|
|
crtc_state->mode_changed = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_check);
|
|
|
|
/**
|
|
* drm_hdmi_connector_mode_valid() - Check if mode is valid for HDMI connector
|
|
* @connector: DRM connector to validate the mode
|
|
* @mode: Display mode to validate
|
|
*
|
|
* Generic .mode_valid implementation for HDMI connectors.
|
|
*/
|
|
enum drm_mode_status
|
|
drm_hdmi_connector_mode_valid(struct drm_connector *connector,
|
|
const struct drm_display_mode *mode)
|
|
{
|
|
unsigned long long clock;
|
|
|
|
clock = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
|
|
if (!clock)
|
|
return MODE_ERROR;
|
|
|
|
return hdmi_clock_valid(connector, mode, clock);
|
|
}
|
|
EXPORT_SYMBOL(drm_hdmi_connector_mode_valid);
|
|
|
|
static int clear_device_infoframe(struct drm_connector *connector,
|
|
enum hdmi_infoframe_type type)
|
|
{
|
|
const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
|
|
struct drm_device *dev = connector->dev;
|
|
int ret;
|
|
|
|
drm_dbg_kms(dev, "Clearing infoframe type 0x%x\n", type);
|
|
|
|
if (!funcs || !funcs->clear_infoframe) {
|
|
drm_dbg_kms(dev, "Function not implemented, bailing.\n");
|
|
return 0;
|
|
}
|
|
|
|
ret = funcs->clear_infoframe(connector, type);
|
|
if (ret) {
|
|
drm_dbg_kms(dev, "Call failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int clear_infoframe(struct drm_connector *connector,
|
|
struct drm_connector_hdmi_infoframe *old_frame)
|
|
{
|
|
int ret;
|
|
|
|
ret = clear_device_infoframe(connector, old_frame->data.any.type);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int write_device_infoframe(struct drm_connector *connector,
|
|
union hdmi_infoframe *frame)
|
|
{
|
|
const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs;
|
|
struct drm_device *dev = connector->dev;
|
|
u8 buffer[HDMI_INFOFRAME_SIZE(MAX)];
|
|
int ret;
|
|
int len;
|
|
|
|
drm_dbg_kms(dev, "Writing infoframe type %x\n", frame->any.type);
|
|
|
|
if (!funcs || !funcs->write_infoframe) {
|
|
drm_dbg_kms(dev, "Function not implemented, bailing.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer));
|
|
if (len < 0)
|
|
return len;
|
|
|
|
ret = funcs->write_infoframe(connector, frame->any.type, buffer, len);
|
|
if (ret) {
|
|
drm_dbg_kms(dev, "Call failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int write_infoframe(struct drm_connector *connector,
|
|
struct drm_connector_hdmi_infoframe *new_frame)
|
|
{
|
|
int ret;
|
|
|
|
ret = write_device_infoframe(connector, &new_frame->data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int write_or_clear_infoframe(struct drm_connector *connector,
|
|
struct drm_connector_hdmi_infoframe *old_frame,
|
|
struct drm_connector_hdmi_infoframe *new_frame)
|
|
{
|
|
if (new_frame->set)
|
|
return write_infoframe(connector, new_frame);
|
|
|
|
if (old_frame->set && !new_frame->set)
|
|
return clear_infoframe(connector, old_frame);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* drm_atomic_helper_connector_hdmi_update_infoframes - Update the Infoframes
|
|
* @connector: A pointer to the HDMI connector
|
|
* @state: The HDMI connector state to generate the infoframe from
|
|
*
|
|
* This function is meant for HDMI connector drivers to write their
|
|
* infoframes. It will typically be used in a
|
|
* @drm_connector_helper_funcs.atomic_enable implementation.
|
|
*
|
|
* Returns:
|
|
* Zero on success, error code on failure.
|
|
*/
|
|
int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
struct drm_connector_state *old_conn_state =
|
|
drm_atomic_get_old_connector_state(state, connector);
|
|
struct drm_connector_state *new_conn_state =
|
|
drm_atomic_get_new_connector_state(state, connector);
|
|
struct drm_display_info *info = &connector->display_info;
|
|
int ret;
|
|
|
|
if (!info->is_hdmi)
|
|
return 0;
|
|
|
|
mutex_lock(&connector->hdmi.infoframes.lock);
|
|
|
|
ret = write_or_clear_infoframe(connector,
|
|
&old_conn_state->hdmi.infoframes.avi,
|
|
&new_conn_state->hdmi.infoframes.avi);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (connector->hdmi.infoframes.audio.set) {
|
|
ret = write_infoframe(connector,
|
|
&connector->hdmi.infoframes.audio);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
ret = write_or_clear_infoframe(connector,
|
|
&old_conn_state->hdmi.infoframes.hdr_drm,
|
|
&new_conn_state->hdmi.infoframes.hdr_drm);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = write_or_clear_infoframe(connector,
|
|
&old_conn_state->hdmi.infoframes.spd,
|
|
&new_conn_state->hdmi.infoframes.spd);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (info->has_hdmi_infoframe) {
|
|
ret = write_or_clear_infoframe(connector,
|
|
&old_conn_state->hdmi.infoframes.hdmi,
|
|
&new_conn_state->hdmi.infoframes.hdmi);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(&connector->hdmi.infoframes.lock);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_infoframes);
|
|
|
|
/**
|
|
* drm_atomic_helper_connector_hdmi_update_audio_infoframe - Update the Audio Infoframe
|
|
* @connector: A pointer to the HDMI connector
|
|
* @frame: A pointer to the audio infoframe to write
|
|
*
|
|
* This function is meant for HDMI connector drivers to update their
|
|
* audio infoframe. It will typically be used in one of the ALSA hooks
|
|
* (most likely prepare).
|
|
*
|
|
* Returns:
|
|
* Zero on success, error code on failure.
|
|
*/
|
|
int
|
|
drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector,
|
|
struct hdmi_audio_infoframe *frame)
|
|
{
|
|
struct drm_connector_hdmi_infoframe *infoframe =
|
|
&connector->hdmi.infoframes.audio;
|
|
struct drm_display_info *info = &connector->display_info;
|
|
int ret;
|
|
|
|
if (!info->is_hdmi)
|
|
return 0;
|
|
|
|
mutex_lock(&connector->hdmi.infoframes.lock);
|
|
|
|
memcpy(&infoframe->data, frame, sizeof(infoframe->data));
|
|
infoframe->set = true;
|
|
|
|
ret = write_infoframe(connector, infoframe);
|
|
|
|
mutex_unlock(&connector->hdmi.infoframes.lock);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_audio_infoframe);
|
|
|
|
/**
|
|
* drm_atomic_helper_connector_hdmi_clear_audio_infoframe - Stop sending the Audio Infoframe
|
|
* @connector: A pointer to the HDMI connector
|
|
*
|
|
* This function is meant for HDMI connector drivers to stop sending their
|
|
* audio infoframe. It will typically be used in one of the ALSA hooks
|
|
* (most likely shutdown).
|
|
*
|
|
* Returns:
|
|
* Zero on success, error code on failure.
|
|
*/
|
|
int
|
|
drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *connector)
|
|
{
|
|
struct drm_connector_hdmi_infoframe *infoframe =
|
|
&connector->hdmi.infoframes.audio;
|
|
struct drm_display_info *info = &connector->display_info;
|
|
int ret;
|
|
|
|
if (!info->is_hdmi)
|
|
return 0;
|
|
|
|
mutex_lock(&connector->hdmi.infoframes.lock);
|
|
|
|
infoframe->set = false;
|
|
|
|
ret = clear_infoframe(connector, infoframe);
|
|
|
|
memset(&infoframe->data, 0, sizeof(infoframe->data));
|
|
|
|
mutex_unlock(&connector->hdmi.infoframes.lock);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_clear_audio_infoframe);
|
|
|
|
static void
|
|
drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector,
|
|
enum drm_connector_status status)
|
|
{
|
|
const struct drm_edid *drm_edid;
|
|
|
|
if (status == connector_status_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;
|
|
}
|
|
|
|
if (connector->hdmi.funcs->read_edid)
|
|
drm_edid = connector->hdmi.funcs->read_edid(connector);
|
|
else
|
|
drm_edid = drm_edid_read(connector);
|
|
|
|
drm_edid_connector_update(connector, drm_edid);
|
|
|
|
drm_edid_free(drm_edid);
|
|
|
|
if (status == connector_status_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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* drm_atomic_helper_connector_hdmi_hotplug - Handle the hotplug event for the HDMI connector
|
|
* @connector: A pointer to the HDMI connector
|
|
* @status: Connection status
|
|
*
|
|
* This function should be called as a part of the .detect() / .detect_ctx()
|
|
* callbacks for all status changes.
|
|
*/
|
|
void drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector,
|
|
enum drm_connector_status status)
|
|
{
|
|
drm_atomic_helper_connector_hdmi_update(connector, status);
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_hotplug);
|
|
|
|
/**
|
|
* drm_atomic_helper_connector_hdmi_force - HDMI Connector implementation of the force callback
|
|
* @connector: A pointer to the HDMI connector
|
|
*
|
|
* This function implements the .force() callback for the HDMI connectors. It
|
|
* can either be used directly as the callback or should be called from within
|
|
* the .force() callback implementation to maintain the HDMI-specific
|
|
* connector's data.
|
|
*/
|
|
void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector)
|
|
{
|
|
drm_atomic_helper_connector_hdmi_update(connector, connector->status);
|
|
}
|
|
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_force);
|