|
| 1 | +From 234cfe71ba316c9666ecd501b7df1a4b5bdb7c03 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Mateusz Kwiatkowski < [email protected]> |
| 3 | +Date: Mon, 11 Oct 2021 23:34:25 +0200 |
| 4 | +Subject: [PATCH 1/2] drm/vc4: Add vertically scaled progressive modes for VEC |
| 5 | + |
| 6 | +The Raspberry Pi firmware, when configured to output progressive |
| 7 | +composite video, scales 720x480/720x576 framebuffer into a |
| 8 | +720x240/720x288 physical video mode. |
| 9 | + |
| 10 | +This commit adds support for replicating such behavior, as this provides |
| 11 | +square-ish virtual pixels, and some userland software rely on this. |
| 12 | + |
| 13 | +Signed-off-by: Mateusz Kwiatkowski < [email protected]> |
| 14 | +--- |
| 15 | + drivers/gpu/drm/vc4/vc4_plane.c | 40 +++++++++++++++++++++ |
| 16 | + drivers/gpu/drm/vc4/vc4_vec.c | 63 ++++++++++++++++++++++++++++++--- |
| 17 | + 2 files changed, 98 insertions(+), 5 deletions(-) |
| 18 | + |
| 19 | +diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c |
| 20 | +index 7947cf47b6e13..96b60fb245982 100644 |
| 21 | +--- a/drivers/gpu/drm/vc4/vc4_plane.c |
| 22 | ++++ b/drivers/gpu/drm/vc4/vc4_plane.c |
| 23 | +@@ -334,6 +334,42 @@ static int vc4_plane_margins_adj(struct drm_plane_state *pstate) |
| 24 | + return 0; |
| 25 | + } |
| 26 | + |
| 27 | ++static int vc4_plane_scaling_adj(struct drm_plane_state *pstate) |
| 28 | ++{ |
| 29 | ++ struct vc4_plane_state *vc4_pstate = to_vc4_plane_state(pstate); |
| 30 | ++ struct drm_crtc_state *crtc_state; |
| 31 | ++ |
| 32 | ++ crtc_state = drm_atomic_get_new_crtc_state(pstate->state, |
| 33 | ++ pstate->crtc); |
| 34 | ++ |
| 35 | ++ if (crtc_state->mode.hdisplay != crtc_state->adjusted_mode.hdisplay) { |
| 36 | ++ vc4_pstate->crtc_x = |
| 37 | ++ DIV_ROUND_CLOSEST(vc4_pstate->crtc_x * |
| 38 | ++ crtc_state->adjusted_mode.hdisplay, |
| 39 | ++ crtc_state->mode.hdisplay); |
| 40 | ++ vc4_pstate->crtc_w = |
| 41 | ++ DIV_ROUND_CLOSEST(vc4_pstate->crtc_w * |
| 42 | ++ crtc_state->adjusted_mode.hdisplay, |
| 43 | ++ crtc_state->mode.hdisplay); |
| 44 | ++ } |
| 45 | ++ |
| 46 | ++ if (crtc_state->mode.vdisplay != crtc_state->adjusted_mode.vdisplay) { |
| 47 | ++ vc4_pstate->crtc_y = |
| 48 | ++ DIV_ROUND_CLOSEST(vc4_pstate->crtc_y * |
| 49 | ++ crtc_state->adjusted_mode.vdisplay, |
| 50 | ++ crtc_state->mode.vdisplay); |
| 51 | ++ vc4_pstate->crtc_h = |
| 52 | ++ DIV_ROUND_CLOSEST(vc4_pstate->crtc_h * |
| 53 | ++ crtc_state->adjusted_mode.vdisplay, |
| 54 | ++ crtc_state->mode.vdisplay); |
| 55 | ++ } |
| 56 | ++ |
| 57 | ++ if (!vc4_pstate->crtc_w || !vc4_pstate->crtc_h) |
| 58 | ++ return -EINVAL; |
| 59 | ++ |
| 60 | ++ return 0; |
| 61 | ++} |
| 62 | ++ |
| 63 | + static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) |
| 64 | + { |
| 65 | + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); |
| 66 | +@@ -378,6 +414,10 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) |
| 67 | + if (ret) |
| 68 | + return ret; |
| 69 | + |
| 70 | ++ ret = vc4_plane_scaling_adj(state); |
| 71 | ++ if (ret) |
| 72 | ++ return ret; |
| 73 | ++ |
| 74 | + vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], |
| 75 | + vc4_state->crtc_w); |
| 76 | + vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0], |
| 77 | +diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c |
| 78 | +index 255e5c6c48e04..14eb28c859eb4 100644 |
| 79 | +--- a/drivers/gpu/drm/vc4/vc4_vec.c |
| 80 | ++++ b/drivers/gpu/drm/vc4/vc4_vec.c |
| 81 | +@@ -253,6 +253,7 @@ enum vc4_vec_tv_mode_id { |
| 82 | + struct vc4_vec_tv_mode { |
| 83 | + const struct drm_display_mode *interlaced_mode; |
| 84 | + const struct drm_display_mode *progressive_mode; |
| 85 | ++ const struct drm_display_mode *scaled_progressive_mode; |
| 86 | + u32 config0; |
| 87 | + u32 config1; |
| 88 | + u32 custom_freq; |
| 89 | +@@ -298,6 +299,12 @@ static const struct drm_display_mode drm_mode_240p = { |
| 90 | + 240, 240 + 3, 240 + 3 + 3, 262, 0, 0) |
| 91 | + }; |
| 92 | + |
| 93 | ++static const struct drm_display_mode drm_mode_scaled_480p = { |
| 94 | ++ DRM_MODE("720x480 (scaled)", DRM_MODE_TYPE_DRIVER, 2 * 13500, |
| 95 | ++ 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, |
| 96 | ++ 2 * 240, 2 * (240 + 3), 2 * (240 + 3 + 3), 2 * 262, 0, 0) |
| 97 | ++}; |
| 98 | ++ |
| 99 | + static const struct drm_display_mode drm_mode_576i = { |
| 100 | + DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, |
| 101 | + 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, |
| 102 | +@@ -311,16 +318,24 @@ static const struct drm_display_mode drm_mode_288p = { |
| 103 | + 288, 288 + 2, 288 + 2 + 3, 312, 0, 0) |
| 104 | + }; |
| 105 | + |
| 106 | ++static const struct drm_display_mode drm_mode_scaled_576p = { |
| 107 | ++ DRM_MODE("720x576 (scaled)", DRM_MODE_TYPE_DRIVER, 2 * 13500, |
| 108 | ++ 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, |
| 109 | ++ 2 * 288, 2 * (288 + 2), 2 * (288 + 2 + 3), 2 * 312, 0, 0) |
| 110 | ++}; |
| 111 | ++ |
| 112 | + static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { |
| 113 | + [VC4_VEC_TV_MODE_NTSC] = { |
| 114 | + .interlaced_mode = &drm_mode_480i, |
| 115 | + .progressive_mode = &drm_mode_240p, |
| 116 | ++ .scaled_progressive_mode = &drm_mode_scaled_480p, |
| 117 | + .config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN, |
| 118 | + .config1 = VEC_CONFIG1_C_CVBS_CVBS, |
| 119 | + }, |
| 120 | + [VC4_VEC_TV_MODE_NTSC_J] = { |
| 121 | + .interlaced_mode = &drm_mode_480i, |
| 122 | + .progressive_mode = &drm_mode_240p, |
| 123 | ++ .scaled_progressive_mode = &drm_mode_scaled_480p, |
| 124 | + .config0 = VEC_CONFIG0_NTSC_STD, |
| 125 | + .config1 = VEC_CONFIG1_C_CVBS_CVBS, |
| 126 | + }, |
| 127 | +@@ -328,6 +343,7 @@ static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { |
| 128 | + /* NTSC with PAL chroma frequency */ |
| 129 | + .interlaced_mode = &drm_mode_480i, |
| 130 | + .progressive_mode = &drm_mode_240p, |
| 131 | ++ .scaled_progressive_mode = &drm_mode_scaled_480p, |
| 132 | + .config0 = VEC_CONFIG0_NTSC_STD, |
| 133 | + .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ, |
| 134 | + .custom_freq = 0x2a098acb, |
| 135 | +@@ -335,18 +351,21 @@ static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { |
| 136 | + [VC4_VEC_TV_MODE_PAL] = { |
| 137 | + .interlaced_mode = &drm_mode_576i, |
| 138 | + .progressive_mode = &drm_mode_288p, |
| 139 | ++ .scaled_progressive_mode = &drm_mode_scaled_576p, |
| 140 | + .config0 = VEC_CONFIG0_PAL_BDGHI_STD, |
| 141 | + .config1 = VEC_CONFIG1_C_CVBS_CVBS, |
| 142 | + }, |
| 143 | + [VC4_VEC_TV_MODE_PAL_M] = { |
| 144 | + .interlaced_mode = &drm_mode_480i, |
| 145 | + .progressive_mode = &drm_mode_240p, |
| 146 | ++ .scaled_progressive_mode = &drm_mode_scaled_480p, |
| 147 | + .config0 = VEC_CONFIG0_PAL_M_STD, |
| 148 | + .config1 = VEC_CONFIG1_C_CVBS_CVBS, |
| 149 | + }, |
| 150 | + [VC4_VEC_TV_MODE_PAL_N] = { |
| 151 | + .interlaced_mode = &drm_mode_576i, |
| 152 | + .progressive_mode = &drm_mode_288p, |
| 153 | ++ .scaled_progressive_mode = &drm_mode_scaled_576p, |
| 154 | + .config0 = VEC_CONFIG0_PAL_N_STD, |
| 155 | + .config1 = VEC_CONFIG1_C_CVBS_CVBS, |
| 156 | + }, |
| 157 | +@@ -354,6 +373,7 @@ static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { |
| 158 | + /* PAL-M with chroma frequency of regular PAL */ |
| 159 | + .interlaced_mode = &drm_mode_480i, |
| 160 | + .progressive_mode = &drm_mode_240p, |
| 161 | ++ .scaled_progressive_mode = &drm_mode_scaled_480p, |
| 162 | + .config0 = VEC_CONFIG0_PAL_M_STD, |
| 163 | + .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ, |
| 164 | + .custom_freq = 0x2a098acb, |
| 165 | +@@ -361,6 +381,7 @@ static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { |
| 166 | + [VC4_VEC_TV_MODE_SECAM] = { |
| 167 | + .interlaced_mode = &drm_mode_576i, |
| 168 | + .progressive_mode = &drm_mode_288p, |
| 169 | ++ .scaled_progressive_mode = &drm_mode_scaled_576p, |
| 170 | + .config0 = VEC_CONFIG0_SECAM_STD, |
| 171 | + .config1 = VEC_CONFIG1_C_CVBS_CVBS, |
| 172 | + .custom_freq = 0x29c71c72, |
| 173 | +@@ -420,7 +441,9 @@ static void vc4_vec_connector_destroy(struct drm_connector *connector) |
| 174 | + static int vc4_vec_connector_get_modes(struct drm_connector *connector) |
| 175 | + { |
| 176 | + struct drm_connector_state *state = connector->state; |
| 177 | +- struct drm_display_mode *interlaced_mode, *progressive_mode; |
| 178 | ++ struct drm_display_mode *interlaced_mode; |
| 179 | ++ struct drm_display_mode *progressive_mode; |
| 180 | ++ struct drm_display_mode *scaled_progressive_mode; |
| 181 | + |
| 182 | + interlaced_mode = |
| 183 | + drm_mode_duplicate(connector->dev, |
| 184 | +@@ -428,24 +451,33 @@ static int vc4_vec_connector_get_modes(struct drm_connector *connector) |
| 185 | + progressive_mode = |
| 186 | + drm_mode_duplicate(connector->dev, |
| 187 | + vc4_vec_tv_modes[state->tv.mode].progressive_mode); |
| 188 | +- if (!interlaced_mode || !progressive_mode) { |
| 189 | ++ scaled_progressive_mode = |
| 190 | ++ drm_mode_duplicate(connector->dev, |
| 191 | ++ vc4_vec_tv_modes[state->tv.mode].scaled_progressive_mode); |
| 192 | ++ if (!interlaced_mode || !progressive_mode || !scaled_progressive_mode) { |
| 193 | + DRM_ERROR("Failed to create a new display mode\n"); |
| 194 | + drm_mode_destroy(connector->dev, interlaced_mode); |
| 195 | + drm_mode_destroy(connector->dev, progressive_mode); |
| 196 | ++ drm_mode_destroy(connector->dev, scaled_progressive_mode); |
| 197 | + return -ENOMEM; |
| 198 | + } |
| 199 | + |
| 200 | + if (connector->cmdline_mode.specified && |
| 201 | + connector->cmdline_mode.refresh_specified && |
| 202 | +- !connector->cmdline_mode.interlace) |
| 203 | ++ !connector->cmdline_mode.interlace) { |
| 204 | + /* progressive mode set at boot, let's make it preferred */ |
| 205 | +- progressive_mode->type |= DRM_MODE_TYPE_PREFERRED; |
| 206 | +- else |
| 207 | ++ if (connector->cmdline_mode.yres > 300) |
| 208 | ++ scaled_progressive_mode->type |= DRM_MODE_TYPE_PREFERRED; |
| 209 | ++ else |
| 210 | ++ progressive_mode->type |= DRM_MODE_TYPE_PREFERRED; |
| 211 | ++ } else { |
| 212 | + /* otherwise, interlaced mode is preferred */ |
| 213 | + interlaced_mode->type |= DRM_MODE_TYPE_PREFERRED; |
| 214 | ++ } |
| 215 | + |
| 216 | + drm_mode_probed_add(connector, interlaced_mode); |
| 217 | + drm_mode_probed_add(connector, progressive_mode); |
| 218 | ++ drm_mode_probed_add(connector, scaled_progressive_mode); |
| 219 | + |
| 220 | + return 1; |
| 221 | + } |
| 222 | +@@ -628,6 +660,27 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder, |
| 223 | + const struct drm_display_mode *reference_mode = |
| 224 | + vc4_vec_tv_modes[conn_state->tv.mode].interlaced_mode; |
| 225 | + |
| 226 | ++ if (!(crtc_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) && |
| 227 | ++ crtc_state->adjusted_mode.vtotal > 312) { |
| 228 | ++ /* vertically scaled progressive mode */ |
| 229 | ++ if (crtc_state->adjusted_mode.crtc_vdisplay % 2 != 0 || |
| 230 | ++ crtc_state->adjusted_mode.crtc_vsync_start % 2 != 0 || |
| 231 | ++ crtc_state->adjusted_mode.crtc_vsync_end % 2 != 0 || |
| 232 | ++ crtc_state->adjusted_mode.crtc_vtotal % 2 != 0) |
| 233 | ++ return -EINVAL; |
| 234 | ++ |
| 235 | ++ crtc_state->adjusted_mode.clock /= 2; |
| 236 | ++ crtc_state->adjusted_mode.crtc_clock /= 2; |
| 237 | ++ crtc_state->adjusted_mode.vdisplay /= 2; |
| 238 | ++ crtc_state->adjusted_mode.crtc_vdisplay /= 2; |
| 239 | ++ crtc_state->adjusted_mode.vsync_start /= 2; |
| 240 | ++ crtc_state->adjusted_mode.crtc_vsync_start /= 2; |
| 241 | ++ crtc_state->adjusted_mode.vsync_end /= 2; |
| 242 | ++ crtc_state->adjusted_mode.crtc_vsync_end /= 2; |
| 243 | ++ crtc_state->adjusted_mode.vtotal /= 2; |
| 244 | ++ crtc_state->adjusted_mode.crtc_vtotal /= 2; |
| 245 | ++ } |
| 246 | ++ |
| 247 | + if (crtc_state->adjusted_mode.crtc_clock != reference_mode->clock || |
| 248 | + crtc_state->adjusted_mode.crtc_htotal != reference_mode->htotal || |
| 249 | + crtc_state->adjusted_mode.crtc_hdisplay % 4 != 0 || |
| 250 | + |
| 251 | +From 3c08549f11d5fb031661d8b1d1b2325ea6153656 Mon Sep 17 00:00:00 2001 |
| 252 | +From: Mateusz Kwiatkowski < [email protected]> |
| 253 | +Date: Tue, 12 Oct 2021 00:30:02 +0200 |
| 254 | +Subject: [PATCH 2/2] drm/vc4: Add support for horizontally caled VEC modes |
| 255 | + |
| 256 | +Composite output uses non-square pixels. By allowing horizontally scaled |
| 257 | +modes, we can simulate a different pixel clock and thus make it possible |
| 258 | +to simulate square pixels at either 4:3 or 16:9 target aspect ratio. |
| 259 | + |
| 260 | +Signed-off-by: Mateusz Kwiatkowski < [email protected]> |
| 261 | +--- |
| 262 | + drivers/gpu/drm/vc4/vc4_vec.c | 40 ++++++++++++++++++++++++++++++----- |
| 263 | + 1 file changed, 35 insertions(+), 5 deletions(-) |
| 264 | + |
| 265 | +diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c |
| 266 | +index 14eb28c859eb4..e60a5c1e01472 100644 |
| 267 | +--- a/drivers/gpu/drm/vc4/vc4_vec.c |
| 268 | ++++ b/drivers/gpu/drm/vc4/vc4_vec.c |
| 269 | +@@ -681,13 +681,43 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder, |
| 270 | + crtc_state->adjusted_mode.crtc_vtotal /= 2; |
| 271 | + } |
| 272 | + |
| 273 | +- if (crtc_state->adjusted_mode.crtc_clock != reference_mode->clock || |
| 274 | +- crtc_state->adjusted_mode.crtc_htotal != reference_mode->htotal || |
| 275 | +- crtc_state->adjusted_mode.crtc_hdisplay % 4 != 0 || |
| 276 | +- crtc_state->adjusted_mode.crtc_hsync_end - |
| 277 | +- crtc_state->adjusted_mode.crtc_hsync_start < 1) |
| 278 | ++ if (crtc_state->adjusted_mode.hdisplay % 4 != 0 || |
| 279 | ++ crtc_state->adjusted_mode.hsync_end - |
| 280 | ++ crtc_state->adjusted_mode.hsync_start < 1) |
| 281 | + return -EINVAL; |
| 282 | + |
| 283 | ++ crtc_state->adjusted_mode.hdisplay = |
| 284 | ++ DIV_ROUND_CLOSEST(crtc_state->adjusted_mode.hdisplay * |
| 285 | ++ reference_mode->clock, |
| 286 | ++ crtc_state->adjusted_mode.clock); |
| 287 | ++ crtc_state->adjusted_mode.hsync_start = |
| 288 | ++ DIV_ROUND_CLOSEST(crtc_state->adjusted_mode.hsync_start * |
| 289 | ++ reference_mode->clock, |
| 290 | ++ crtc_state->adjusted_mode.clock); |
| 291 | ++ crtc_state->adjusted_mode.hsync_end = |
| 292 | ++ DIV_ROUND_CLOSEST(crtc_state->adjusted_mode.hsync_end * |
| 293 | ++ reference_mode->clock, |
| 294 | ++ crtc_state->adjusted_mode.clock); |
| 295 | ++ crtc_state->adjusted_mode.htotal = |
| 296 | ++ DIV_ROUND_CLOSEST(crtc_state->adjusted_mode.htotal * |
| 297 | ++ reference_mode->clock, |
| 298 | ++ crtc_state->adjusted_mode.clock); |
| 299 | ++ crtc_state->adjusted_mode.clock = reference_mode->clock; |
| 300 | ++ |
| 301 | ++ if (crtc_state->adjusted_mode.htotal != reference_mode->htotal) |
| 302 | ++ return -EINVAL; |
| 303 | ++ |
| 304 | ++ if (crtc_state->adjusted_mode.hsync_end - |
| 305 | ++ crtc_state->adjusted_mode.hsync_start < 1) |
| 306 | ++ crtc_state->adjusted_mode.hsync_end = |
| 307 | ++ crtc_state->adjusted_mode.hsync_start + 1; |
| 308 | ++ |
| 309 | ++ crtc_state->adjusted_mode.crtc_clock = crtc_state->adjusted_mode.clock; |
| 310 | ++ crtc_state->adjusted_mode.crtc_hdisplay = crtc_state->adjusted_mode.hdisplay; |
| 311 | ++ crtc_state->adjusted_mode.crtc_hsync_start = crtc_state->adjusted_mode.hsync_start; |
| 312 | ++ crtc_state->adjusted_mode.crtc_hsync_end = crtc_state->adjusted_mode.hsync_end; |
| 313 | ++ crtc_state->adjusted_mode.crtc_htotal = crtc_state->adjusted_mode.htotal; |
| 314 | ++ |
| 315 | + switch (reference_mode->vtotal) { |
| 316 | + case 525: |
| 317 | + if (crtc_state->adjusted_mode.crtc_vdisplay < 1 || |
0 commit comments