Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions drivers/gpu/drm/display/drm_hdmi_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions drivers/gpu/drm/drm_edid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
22 changes: 22 additions & 0 deletions drivers/gpu/drm/tests/drm_connector_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down
7 changes: 4 additions & 3 deletions drivers/gpu/drm/vc4/vc4_crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
5 changes: 4 additions & 1 deletion drivers/gpu/drm/vc4/vc4_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;

Expand Down
20 changes: 14 additions & 6 deletions drivers/gpu/drm/vc4/vc4_hvs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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));
Expand All @@ -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);
Expand Down
Loading