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

Configure Feed

Select the types of activity you want to include in your feed.

drm/connector: hdmi: Compute bpc and format automatically

Now that we have all the infrastructure needed, we can add some code
that will, for a given connector state and mode, compute the best output
format and bpc.

The algorithm is equivalent to the one already found in i915 and vc4.

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20240527-kms-hdmi-connector-state-v15-15-c5af16c3aae2@kernel.org
Signed-off-by: Maxime Ripard <mripard@kernel.org>

+230 -12
+214 -3
drivers/gpu/drm/display/drm_hdmi_state_helper.c
··· 2 2 3 3 #include <drm/drm_atomic.h> 4 4 #include <drm/drm_connector.h> 5 + #include <drm/drm_edid.h> 6 + #include <drm/drm_print.h> 5 7 6 8 #include <drm/display/drm_hdmi_helper.h> 7 9 #include <drm/display/drm_hdmi_state_helper.h> ··· 50 48 return &crtc_state->mode; 51 49 } 52 50 51 + static bool 52 + sink_supports_format_bpc(const struct drm_connector *connector, 53 + const struct drm_display_info *info, 54 + const struct drm_display_mode *mode, 55 + unsigned int format, unsigned int bpc) 56 + { 57 + struct drm_device *dev = connector->dev; 58 + u8 vic = drm_match_cea_mode(mode); 59 + 60 + /* 61 + * CTA-861-F, section 5.4 - Color Coding & Quantization states 62 + * that the bpc must be 8, 10, 12 or 16 except for the default 63 + * 640x480 VIC1 where the value must be 8. 64 + * 65 + * The definition of default here is ambiguous but the spec 66 + * refers to VIC1 being the default timing in several occasions 67 + * so our understanding is that for the default timing (ie, 68 + * VIC1), the bpc must be 8. 69 + */ 70 + if (vic == 1 && bpc != 8) { 71 + drm_dbg_kms(dev, "VIC1 requires a bpc of 8, got %u\n", bpc); 72 + return false; 73 + } 74 + 75 + if (!info->is_hdmi && 76 + (format != HDMI_COLORSPACE_RGB || bpc != 8)) { 77 + drm_dbg_kms(dev, "DVI Monitors require an RGB output at 8 bpc\n"); 78 + return false; 79 + } 80 + 81 + if (!(connector->hdmi.supported_formats & BIT(format))) { 82 + drm_dbg_kms(dev, "%s format unsupported by the connector.\n", 83 + drm_hdmi_connector_get_output_format_name(format)); 84 + return false; 85 + } 86 + 87 + switch (format) { 88 + case HDMI_COLORSPACE_RGB: 89 + drm_dbg_kms(dev, "RGB Format, checking the constraints.\n"); 90 + 91 + /* 92 + * In some cases, like when the EDID readout fails, or 93 + * is not an HDMI compliant EDID for some reason, the 94 + * color_formats field will be blank and not report any 95 + * format supported. In such a case, assume that RGB is 96 + * supported so we can keep things going and light up 97 + * the display. 98 + */ 99 + if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444)) 100 + drm_warn(dev, "HDMI Sink doesn't support RGB, something's wrong.\n"); 101 + 102 + if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) { 103 + drm_dbg_kms(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); 104 + return false; 105 + } 106 + 107 + if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) { 108 + drm_dbg_kms(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); 109 + return false; 110 + } 111 + 112 + drm_dbg_kms(dev, "RGB format supported in that configuration.\n"); 113 + 114 + return true; 115 + 116 + case HDMI_COLORSPACE_YUV420: 117 + /* TODO: YUV420 is unsupported at the moment. */ 118 + drm_dbg_kms(dev, "YUV420 format isn't supported yet.\n"); 119 + return false; 120 + 121 + case HDMI_COLORSPACE_YUV422: 122 + drm_dbg_kms(dev, "YUV422 format, checking the constraints.\n"); 123 + 124 + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) { 125 + drm_dbg_kms(dev, "Sink doesn't support YUV422.\n"); 126 + return false; 127 + } 128 + 129 + if (bpc > 12) { 130 + drm_dbg_kms(dev, "YUV422 only supports 12 bpc or lower.\n"); 131 + return false; 132 + } 133 + 134 + /* 135 + * HDMI Spec 1.3 - Section 6.5 Pixel Encodings and Color Depth 136 + * states that Deep Color is not relevant for YUV422 so we 137 + * don't need to check the Deep Color bits in the EDIDs here. 138 + */ 139 + 140 + drm_dbg_kms(dev, "YUV422 format supported in that configuration.\n"); 141 + 142 + return true; 143 + 144 + case HDMI_COLORSPACE_YUV444: 145 + drm_dbg_kms(dev, "YUV444 format, checking the constraints.\n"); 146 + 147 + if (!(info->color_formats & DRM_COLOR_FORMAT_YCBCR444)) { 148 + drm_dbg_kms(dev, "Sink doesn't support YUV444.\n"); 149 + return false; 150 + } 151 + 152 + if (bpc == 10 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_30)) { 153 + drm_dbg_kms(dev, "10 BPC but sink doesn't support Deep Color 30.\n"); 154 + return false; 155 + } 156 + 157 + if (bpc == 12 && !(info->edid_hdmi_ycbcr444_dc_modes & DRM_EDID_HDMI_DC_36)) { 158 + drm_dbg_kms(dev, "12 BPC but sink doesn't support Deep Color 36.\n"); 159 + return false; 160 + } 161 + 162 + drm_dbg_kms(dev, "YUV444 format supported in that configuration.\n"); 163 + 164 + return true; 165 + } 166 + 167 + drm_dbg_kms(dev, "Unsupported pixel format.\n"); 168 + return false; 169 + } 170 + 53 171 static enum drm_mode_status 54 172 hdmi_clock_valid(const struct drm_connector *connector, 55 173 const struct drm_display_mode *mode, ··· 214 92 return 0; 215 93 } 216 94 95 + static bool 96 + hdmi_try_format_bpc(const struct drm_connector *connector, 97 + struct drm_connector_state *conn_state, 98 + const struct drm_display_mode *mode, 99 + unsigned int bpc, enum hdmi_colorspace fmt) 100 + { 101 + const struct drm_display_info *info = &connector->display_info; 102 + struct drm_device *dev = connector->dev; 103 + int ret; 104 + 105 + drm_dbg_kms(dev, "Trying %s output format\n", 106 + drm_hdmi_connector_get_output_format_name(fmt)); 107 + 108 + if (!sink_supports_format_bpc(connector, info, mode, fmt, bpc)) { 109 + drm_dbg_kms(dev, "%s output format not supported with %u bpc\n", 110 + drm_hdmi_connector_get_output_format_name(fmt), 111 + bpc); 112 + return false; 113 + } 114 + 115 + ret = hdmi_compute_clock(connector, conn_state, mode, bpc, fmt); 116 + if (ret) { 117 + drm_dbg_kms(dev, "Couldn't compute clock for %s output format and %u bpc\n", 118 + drm_hdmi_connector_get_output_format_name(fmt), 119 + bpc); 120 + return false; 121 + } 122 + 123 + drm_dbg_kms(dev, "%s output format supported with %u (TMDS char rate: %llu Hz)\n", 124 + drm_hdmi_connector_get_output_format_name(fmt), 125 + bpc, conn_state->hdmi.tmds_char_rate); 126 + 127 + return true; 128 + } 129 + 130 + static int 131 + hdmi_compute_format(const struct drm_connector *connector, 132 + struct drm_connector_state *conn_state, 133 + const struct drm_display_mode *mode, 134 + unsigned int bpc) 135 + { 136 + struct drm_device *dev = connector->dev; 137 + 138 + /* 139 + * TODO: Add support for YCbCr420 output for HDMI 2.0 capable 140 + * devices, for modes that only support YCbCr420. 141 + */ 142 + if (hdmi_try_format_bpc(connector, conn_state, mode, bpc, HDMI_COLORSPACE_RGB)) { 143 + conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB; 144 + return 0; 145 + } 146 + 147 + drm_dbg_kms(dev, "Failed. No Format Supported for that bpc count.\n"); 148 + 149 + return -EINVAL; 150 + } 151 + 152 + static int 153 + hdmi_compute_config(const struct drm_connector *connector, 154 + struct drm_connector_state *conn_state, 155 + const struct drm_display_mode *mode) 156 + { 157 + struct drm_device *dev = connector->dev; 158 + unsigned int max_bpc = clamp_t(unsigned int, 159 + conn_state->max_bpc, 160 + 8, connector->max_bpc); 161 + unsigned int bpc; 162 + int ret; 163 + 164 + for (bpc = max_bpc; bpc >= 8; bpc -= 2) { 165 + drm_dbg_kms(dev, "Trying with a %d bpc output\n", bpc); 166 + 167 + ret = hdmi_compute_format(connector, conn_state, mode, bpc); 168 + if (ret) 169 + continue; 170 + 171 + conn_state->hdmi.output_bpc = bpc; 172 + 173 + drm_dbg_kms(dev, 174 + "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n", 175 + mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode), 176 + conn_state->hdmi.output_bpc, 177 + drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format), 178 + conn_state->hdmi.tmds_char_rate); 179 + 180 + return 0; 181 + } 182 + 183 + return -EINVAL; 184 + } 185 + 217 186 /** 218 187 * drm_atomic_helper_connector_hdmi_check() - Helper to check HDMI connector atomic state 219 188 * @connector: DRM Connector ··· 328 115 connector_state_get_mode(new_conn_state); 329 116 int ret; 330 117 331 - ret = hdmi_compute_clock(connector, new_conn_state, mode, 332 - new_conn_state->hdmi.output_bpc, 333 - new_conn_state->hdmi.output_format); 118 + ret = hdmi_compute_config(connector, new_conn_state, mode); 334 119 if (ret) 335 120 return ret; 336 121
+16 -9
drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c
··· 72 72 conn_state = drm_atomic_get_connector_state(state, connector); 73 73 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); 74 74 75 - conn_state->hdmi.output_bpc = connector->max_bpc; 76 - conn_state->hdmi.output_format = HDMI_COLORSPACE_RGB; 77 - 78 75 ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); 79 76 KUNIT_EXPECT_EQ(test, ret, 0); 80 77 ··· 250 253 10); 251 254 KUNIT_ASSERT_NOT_NULL(test, priv); 252 255 256 + conn = &priv->connector; 257 + ret = set_connector_edid(test, conn, 258 + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, 259 + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); 260 + KUNIT_ASSERT_EQ(test, ret, 0); 261 + 253 262 ctx = drm_kunit_helper_acquire_ctx_alloc(test); 254 263 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); 255 264 256 - conn = &priv->connector; 257 265 preferred = find_preferred_mode(conn); 258 266 KUNIT_ASSERT_NOT_NULL(test, preferred); 259 267 ··· 276 274 old_conn_state = drm_atomic_get_old_connector_state(state, conn); 277 275 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_conn_state); 278 276 279 - new_conn_state->hdmi.output_bpc = 8; 277 + new_conn_state->max_requested_bpc = 8; 280 278 281 279 KUNIT_ASSERT_NE(test, 282 - old_conn_state->hdmi.output_bpc, 283 - new_conn_state->hdmi.output_bpc); 280 + old_conn_state->max_requested_bpc, 281 + new_conn_state->max_requested_bpc); 284 282 285 283 ret = drm_atomic_check_only(state); 286 284 KUNIT_ASSERT_EQ(test, ret, 0); ··· 324 322 10); 325 323 KUNIT_ASSERT_NOT_NULL(test, priv); 326 324 325 + conn = &priv->connector; 326 + ret = set_connector_edid(test, conn, 327 + test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz, 328 + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz)); 329 + KUNIT_ASSERT_EQ(test, ret, 0); 330 + 327 331 ctx = drm_kunit_helper_acquire_ctx_alloc(test); 328 332 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); 329 333 330 - conn = &priv->connector; 331 334 preferred = find_preferred_mode(conn); 332 335 KUNIT_ASSERT_NOT_NULL(test, preferred); 333 336 ··· 679 672 680 673 conn = &priv->connector; 681 674 conn_state = conn->state; 682 - KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, HDMI_COLORSPACE_RGB); 675 + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, 0); 683 676 } 684 677 685 678 /*