diff --git a/drivers/gpu/drm/display/drm_hdmi_helper.c b/drivers/gpu/drm/display/drm_hdmi_helper.c index a237dc55805d0d..1534b1a4795ba9 100644 --- a/drivers/gpu/drm/display/drm_hdmi_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_helper.c @@ -254,6 +254,14 @@ drm_hdmi_compute_mode_clock(const struct drm_display_mode *mode, if (mode->flags & DRM_MODE_FLAG_DBLCLK) clock = clock * 2; + /* + * HDMI 1.4b Spec, Section 8.2.3.2 - Frame Packing Structure + * transmits both eyes within a single frame, at twice the vertical + * total, and thus twice the pixel clock, of the base 2D mode. + */ + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock = clock * 2; + return DIV_ROUND_CLOSEST_ULL(clock * bpc, 8); } EXPORT_SYMBOL(drm_hdmi_compute_mode_clock); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 28069e5863679f..8f777ca837271e 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4920,8 +4920,8 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) newflag = DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; break; case 8: - /* 3D_Detail_X */ - if ((db[9 + offset + i] >> 4) == 1) + /* 3D_Detail_X: 0 and 1 both include horizontal sub-sampling */ + if ((db[9 + offset + i] >> 4) <= 1) newflag = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; break; } diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index 22e2d959eb3145..6cfdce96feb8bf 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -1625,6 +1625,27 @@ static void drm_test_drm_hdmi_compute_mode_clock_rgb_double(struct kunit *test) KUNIT_EXPECT_EQ(test, (mode->clock * 1000ULL) * 2, rate); } +/* + * Test that for a frame packing stereo mode, the TMDS character rate is + * indeed double the mode pixel clock. + */ +static void drm_test_drm_hdmi_compute_mode_clock_stereo_frame_packing(struct kunit *test) +{ + struct drm_connector_init_priv *priv = test->priv; + struct drm_display_mode *mode; + unsigned long long rate; + struct drm_device *drm = &priv->drm; + + mode = drm_kunit_display_mode_from_cea_vic(test, drm, 32); + KUNIT_ASSERT_NOT_NULL(test, mode); + + mode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING; + + rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB); + KUNIT_ASSERT_GT(test, rate, 0); + KUNIT_EXPECT_EQ(test, (mode->clock * 1000ULL) * 2, rate); +} + /* * Test that the TMDS character rate computation for the VIC modes * explicitly listed in the spec as supporting YUV420 succeed and return @@ -1784,6 +1805,7 @@ static struct kunit_case drm_hdmi_compute_mode_clock_tests[] = { KUNIT_CASE(drm_test_drm_hdmi_compute_mode_clock_rgb_12bpc), KUNIT_CASE(drm_test_drm_hdmi_compute_mode_clock_rgb_12bpc_vic_1), KUNIT_CASE(drm_test_drm_hdmi_compute_mode_clock_rgb_double), + KUNIT_CASE(drm_test_drm_hdmi_compute_mode_clock_stereo_frame_packing), KUNIT_CASE_PARAM(drm_test_connector_hdmi_compute_mode_clock_yuv420_valid, drm_hdmi_compute_mode_clock_yuv420_valid_gen_params), KUNIT_CASE(drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc), diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 64265651dbb2e0..9de390fe3794cd 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -768,10 +768,11 @@ int vc4_crtc_atomic_check(struct drm_crtc *crtc, struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); if (vc4_encoder->type == VC4_ENCODER_TYPE_HDMI0) { - vc4_state->hvs_load = max(mode->clock * mode->hdisplay / mode->htotal + 8000, - mode->clock * 9 / 10) * 1000; + vc4_state->hvs_load = + max(mode->crtc_clock * mode->hdisplay / mode->htotal + 8000, + mode->crtc_clock * 9 / 10) * 1000; } else { - vc4_state->hvs_load = mode->clock * 1000; + vc4_state->hvs_load = mode->crtc_clock * 1000; } } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index c111b61175f044..5833a6997fd66b 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -1730,6 +1730,9 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, tmds_char_rate = mode->clock * 1000; } + /* Rebuilds every crtc_* field, so CRTC_INTERLACE_HALVE_V is needed too */ + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); + return 0; } @@ -1853,7 +1856,7 @@ static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi, unsigned int samplerat lockdep_assert_held(&vc4_hdmi->hw_lock); n = 128 * samplerate / 1000; - tmp = (u64)(mode->clock * 1000) * n; + tmp = (u64)(mode->crtc_clock * 1000) * n; do_div(tmp, 128 * samplerate); cts = tmp; diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index 4ae905855bb466..aa51aba723a38d 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -945,6 +945,7 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); unsigned int chan = vc4_crtc_state->assigned_channel; bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; + int hdisplay, vdisplay; u32 dispbkgndx; u32 dispctrl; int idx; @@ -954,6 +955,9 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, if (!drm_dev_enter(drm, &idx)) return -ENODEV; + /* Doubled for frame packing stereo modes */ + drm_mode_get_hv_timing(mode, &hdisplay, &vdisplay); + HVS_WRITE(SCALER_DISPCTRLX(chan), 0); HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET); HVS_WRITE(SCALER_DISPCTRLX(chan), 0); @@ -967,16 +971,16 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan)); if (vc4->gen == VC4_GEN_4) { - dispctrl |= VC4_SET_FIELD(mode->hdisplay, + dispctrl |= VC4_SET_FIELD(hdisplay, SCALER_DISPCTRLX_WIDTH) | - VC4_SET_FIELD(mode->vdisplay, + VC4_SET_FIELD(vdisplay, SCALER_DISPCTRLX_HEIGHT) | (oneshot ? SCALER_DISPCTRLX_ONESHOT : 0); dispbkgndx |= SCALER_DISPBKGND_AUTOHS; } else { - dispctrl |= VC4_SET_FIELD(mode->hdisplay, + dispctrl |= VC4_SET_FIELD(hdisplay, SCALER5_DISPCTRLX_WIDTH) | - VC4_SET_FIELD(mode->vdisplay, + VC4_SET_FIELD(vdisplay, SCALER5_DISPCTRLX_HEIGHT) | (oneshot ? SCALER5_DISPCTRLX_ONESHOT : 0); dispbkgndx &= ~SCALER5_DISPBKGND_BCK2BCK; @@ -1009,6 +1013,7 @@ static int vc6_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); unsigned int chan = vc4_crtc_state->assigned_channel; bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; + int hdisplay, vdisplay; u32 disp_ctrl1; int idx; @@ -1017,6 +1022,9 @@ static int vc6_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, if (!drm_dev_enter(drm, &idx)) return -ENODEV; + /* Doubled for frame packing stereo modes */ + drm_mode_get_hv_timing(mode, &hdisplay, &vdisplay); + HVS_WRITE(SCALER6_DISPX_CTRL0(chan), SCALER6_DISPX_CTRL0_RESET); disp_ctrl1 = HVS_READ(SCALER6_DISPX_CTRL1(chan)); @@ -1026,10 +1034,10 @@ static int vc6_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, HVS_WRITE(SCALER6_DISPX_CTRL0(chan), SCALER6_DISPX_CTRL0_ENB | - VC4_SET_FIELD(mode->hdisplay - 1, + VC4_SET_FIELD(hdisplay - 1, SCALER6_DISPX_CTRL0_FWIDTH) | (oneshot ? SCALER6_DISPX_CTRL0_ONESHOT : 0) | - VC4_SET_FIELD(mode->vdisplay - 1, + VC4_SET_FIELD(vdisplay - 1, SCALER6_DISPX_CTRL0_LINES)); drm_dev_exit(idx);