Skip to content

Commit 234cfe7

Browse files
Mateusz KwiatkowskiMateusz Kwiatkowski
authored andcommitted
drm/vc4: Add vertically scaled progressive modes for VEC
The Raspberry Pi firmware, when configured to output progressive composite video, scales 720x480/720x576 framebuffer into a 720x240/720x288 physical video mode. This commit adds support for replicating such behavior, as this provides square-ish virtual pixels, and some userland software rely on this. Signed-off-by: Mateusz Kwiatkowski <[email protected]>
1 parent 4ae4769 commit 234cfe7

File tree

2 files changed

+98
-5
lines changed

2 files changed

+98
-5
lines changed

drivers/gpu/drm/vc4/vc4_plane.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,42 @@ static int vc4_plane_margins_adj(struct drm_plane_state *pstate)
334334
return 0;
335335
}
336336

337+
static int vc4_plane_scaling_adj(struct drm_plane_state *pstate)
338+
{
339+
struct vc4_plane_state *vc4_pstate = to_vc4_plane_state(pstate);
340+
struct drm_crtc_state *crtc_state;
341+
342+
crtc_state = drm_atomic_get_new_crtc_state(pstate->state,
343+
pstate->crtc);
344+
345+
if (crtc_state->mode.hdisplay != crtc_state->adjusted_mode.hdisplay) {
346+
vc4_pstate->crtc_x =
347+
DIV_ROUND_CLOSEST(vc4_pstate->crtc_x *
348+
crtc_state->adjusted_mode.hdisplay,
349+
crtc_state->mode.hdisplay);
350+
vc4_pstate->crtc_w =
351+
DIV_ROUND_CLOSEST(vc4_pstate->crtc_w *
352+
crtc_state->adjusted_mode.hdisplay,
353+
crtc_state->mode.hdisplay);
354+
}
355+
356+
if (crtc_state->mode.vdisplay != crtc_state->adjusted_mode.vdisplay) {
357+
vc4_pstate->crtc_y =
358+
DIV_ROUND_CLOSEST(vc4_pstate->crtc_y *
359+
crtc_state->adjusted_mode.vdisplay,
360+
crtc_state->mode.vdisplay);
361+
vc4_pstate->crtc_h =
362+
DIV_ROUND_CLOSEST(vc4_pstate->crtc_h *
363+
crtc_state->adjusted_mode.vdisplay,
364+
crtc_state->mode.vdisplay);
365+
}
366+
367+
if (!vc4_pstate->crtc_w || !vc4_pstate->crtc_h)
368+
return -EINVAL;
369+
370+
return 0;
371+
}
372+
337373
static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
338374
{
339375
struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
@@ -378,6 +414,10 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
378414
if (ret)
379415
return ret;
380416

417+
ret = vc4_plane_scaling_adj(state);
418+
if (ret)
419+
return ret;
420+
381421
vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0],
382422
vc4_state->crtc_w);
383423
vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0],

drivers/gpu/drm/vc4/vc4_vec.c

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ enum vc4_vec_tv_mode_id {
253253
struct vc4_vec_tv_mode {
254254
const struct drm_display_mode *interlaced_mode;
255255
const struct drm_display_mode *progressive_mode;
256+
const struct drm_display_mode *scaled_progressive_mode;
256257
u32 config0;
257258
u32 config1;
258259
u32 custom_freq;
@@ -298,6 +299,12 @@ static const struct drm_display_mode drm_mode_240p = {
298299
240, 240 + 3, 240 + 3 + 3, 262, 0, 0)
299300
};
300301

302+
static const struct drm_display_mode drm_mode_scaled_480p = {
303+
DRM_MODE("720x480 (scaled)", DRM_MODE_TYPE_DRIVER, 2 * 13500,
304+
720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
305+
2 * 240, 2 * (240 + 3), 2 * (240 + 3 + 3), 2 * 262, 0, 0)
306+
};
307+
301308
static const struct drm_display_mode drm_mode_576i = {
302309
DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
303310
720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
@@ -311,56 +318,70 @@ static const struct drm_display_mode drm_mode_288p = {
311318
288, 288 + 2, 288 + 2 + 3, 312, 0, 0)
312319
};
313320

321+
static const struct drm_display_mode drm_mode_scaled_576p = {
322+
DRM_MODE("720x576 (scaled)", DRM_MODE_TYPE_DRIVER, 2 * 13500,
323+
720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
324+
2 * 288, 2 * (288 + 2), 2 * (288 + 2 + 3), 2 * 312, 0, 0)
325+
};
326+
314327
static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
315328
[VC4_VEC_TV_MODE_NTSC] = {
316329
.interlaced_mode = &drm_mode_480i,
317330
.progressive_mode = &drm_mode_240p,
331+
.scaled_progressive_mode = &drm_mode_scaled_480p,
318332
.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
319333
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
320334
},
321335
[VC4_VEC_TV_MODE_NTSC_J] = {
322336
.interlaced_mode = &drm_mode_480i,
323337
.progressive_mode = &drm_mode_240p,
338+
.scaled_progressive_mode = &drm_mode_scaled_480p,
324339
.config0 = VEC_CONFIG0_NTSC_STD,
325340
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
326341
},
327342
[VC4_VEC_TV_MODE_NTSC_443] = {
328343
/* NTSC with PAL chroma frequency */
329344
.interlaced_mode = &drm_mode_480i,
330345
.progressive_mode = &drm_mode_240p,
346+
.scaled_progressive_mode = &drm_mode_scaled_480p,
331347
.config0 = VEC_CONFIG0_NTSC_STD,
332348
.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
333349
.custom_freq = 0x2a098acb,
334350
},
335351
[VC4_VEC_TV_MODE_PAL] = {
336352
.interlaced_mode = &drm_mode_576i,
337353
.progressive_mode = &drm_mode_288p,
354+
.scaled_progressive_mode = &drm_mode_scaled_576p,
338355
.config0 = VEC_CONFIG0_PAL_BDGHI_STD,
339356
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
340357
},
341358
[VC4_VEC_TV_MODE_PAL_M] = {
342359
.interlaced_mode = &drm_mode_480i,
343360
.progressive_mode = &drm_mode_240p,
361+
.scaled_progressive_mode = &drm_mode_scaled_480p,
344362
.config0 = VEC_CONFIG0_PAL_M_STD,
345363
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
346364
},
347365
[VC4_VEC_TV_MODE_PAL_N] = {
348366
.interlaced_mode = &drm_mode_576i,
349367
.progressive_mode = &drm_mode_288p,
368+
.scaled_progressive_mode = &drm_mode_scaled_576p,
350369
.config0 = VEC_CONFIG0_PAL_N_STD,
351370
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
352371
},
353372
[VC4_VEC_TV_MODE_PAL60] = {
354373
/* PAL-M with chroma frequency of regular PAL */
355374
.interlaced_mode = &drm_mode_480i,
356375
.progressive_mode = &drm_mode_240p,
376+
.scaled_progressive_mode = &drm_mode_scaled_480p,
357377
.config0 = VEC_CONFIG0_PAL_M_STD,
358378
.config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
359379
.custom_freq = 0x2a098acb,
360380
},
361381
[VC4_VEC_TV_MODE_SECAM] = {
362382
.interlaced_mode = &drm_mode_576i,
363383
.progressive_mode = &drm_mode_288p,
384+
.scaled_progressive_mode = &drm_mode_scaled_576p,
364385
.config0 = VEC_CONFIG0_SECAM_STD,
365386
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
366387
.custom_freq = 0x29c71c72,
@@ -420,32 +441,43 @@ static void vc4_vec_connector_destroy(struct drm_connector *connector)
420441
static int vc4_vec_connector_get_modes(struct drm_connector *connector)
421442
{
422443
struct drm_connector_state *state = connector->state;
423-
struct drm_display_mode *interlaced_mode, *progressive_mode;
444+
struct drm_display_mode *interlaced_mode;
445+
struct drm_display_mode *progressive_mode;
446+
struct drm_display_mode *scaled_progressive_mode;
424447

425448
interlaced_mode =
426449
drm_mode_duplicate(connector->dev,
427450
vc4_vec_tv_modes[state->tv.mode].interlaced_mode);
428451
progressive_mode =
429452
drm_mode_duplicate(connector->dev,
430453
vc4_vec_tv_modes[state->tv.mode].progressive_mode);
431-
if (!interlaced_mode || !progressive_mode) {
454+
scaled_progressive_mode =
455+
drm_mode_duplicate(connector->dev,
456+
vc4_vec_tv_modes[state->tv.mode].scaled_progressive_mode);
457+
if (!interlaced_mode || !progressive_mode || !scaled_progressive_mode) {
432458
DRM_ERROR("Failed to create a new display mode\n");
433459
drm_mode_destroy(connector->dev, interlaced_mode);
434460
drm_mode_destroy(connector->dev, progressive_mode);
461+
drm_mode_destroy(connector->dev, scaled_progressive_mode);
435462
return -ENOMEM;
436463
}
437464

438465
if (connector->cmdline_mode.specified &&
439466
connector->cmdline_mode.refresh_specified &&
440-
!connector->cmdline_mode.interlace)
467+
!connector->cmdline_mode.interlace) {
441468
/* progressive mode set at boot, let's make it preferred */
442-
progressive_mode->type |= DRM_MODE_TYPE_PREFERRED;
443-
else
469+
if (connector->cmdline_mode.yres > 300)
470+
scaled_progressive_mode->type |= DRM_MODE_TYPE_PREFERRED;
471+
else
472+
progressive_mode->type |= DRM_MODE_TYPE_PREFERRED;
473+
} else {
444474
/* otherwise, interlaced mode is preferred */
445475
interlaced_mode->type |= DRM_MODE_TYPE_PREFERRED;
476+
}
446477

447478
drm_mode_probed_add(connector, interlaced_mode);
448479
drm_mode_probed_add(connector, progressive_mode);
480+
drm_mode_probed_add(connector, scaled_progressive_mode);
449481

450482
return 1;
451483
}
@@ -628,6 +660,27 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
628660
const struct drm_display_mode *reference_mode =
629661
vc4_vec_tv_modes[conn_state->tv.mode].interlaced_mode;
630662

663+
if (!(crtc_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) &&
664+
crtc_state->adjusted_mode.vtotal > 312) {
665+
/* vertically scaled progressive mode */
666+
if (crtc_state->adjusted_mode.crtc_vdisplay % 2 != 0 ||
667+
crtc_state->adjusted_mode.crtc_vsync_start % 2 != 0 ||
668+
crtc_state->adjusted_mode.crtc_vsync_end % 2 != 0 ||
669+
crtc_state->adjusted_mode.crtc_vtotal % 2 != 0)
670+
return -EINVAL;
671+
672+
crtc_state->adjusted_mode.clock /= 2;
673+
crtc_state->adjusted_mode.crtc_clock /= 2;
674+
crtc_state->adjusted_mode.vdisplay /= 2;
675+
crtc_state->adjusted_mode.crtc_vdisplay /= 2;
676+
crtc_state->adjusted_mode.vsync_start /= 2;
677+
crtc_state->adjusted_mode.crtc_vsync_start /= 2;
678+
crtc_state->adjusted_mode.vsync_end /= 2;
679+
crtc_state->adjusted_mode.crtc_vsync_end /= 2;
680+
crtc_state->adjusted_mode.vtotal /= 2;
681+
crtc_state->adjusted_mode.crtc_vtotal /= 2;
682+
}
683+
631684
if (crtc_state->adjusted_mode.crtc_clock != reference_mode->clock ||
632685
crtc_state->adjusted_mode.crtc_htotal != reference_mode->htotal ||
633686
crtc_state->adjusted_mode.crtc_hdisplay % 4 != 0 ||

0 commit comments

Comments
 (0)