fb20dfe94df24812d98adf207de4f0b1e7f3d3b4
[openwrt/staging/xback.git] /
1 From 04b88e1a8b867b045bc90d997e8e0260b00dbb15 Mon Sep 17 00:00:00 2001
2 From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
3 Date: Thu, 11 Jan 2024 12:13:03 +0000
4 Subject: [PATCH] drm: rp1: rp1-vec: Allow non-standard modes with various
5 crops
6
7 Tweak sync timings in the advertised modelines.
8
9 Accept other, custom modes, provided they fit within the active
10 area of one of the existing hardware-supported TV modes.
11
12 Instead of always padding symmetrically, try to respect the user's
13 [hv]sync_start values, allowing the image to be shifted around
14 the screen (to fine-tune overscan correction).
15
16 Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
17 ---
18 drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c | 66 ++++++++++++---------
19 drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c | 74 ++++++++++++++++++------
20 2 files changed, 96 insertions(+), 44 deletions(-)
21
22 --- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
23 +++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
24 @@ -19,7 +19,6 @@
25 #include <linux/list.h>
26 #include <linux/platform_device.h>
27 #include <linux/clk.h>
28 -#include <linux/printk.h>
29 #include <linux/console.h>
30 #include <linux/debugfs.h>
31 #include <linux/uaccess.h>
32 @@ -208,26 +207,26 @@ static void rp1vec_connector_destroy(str
33 static const struct drm_display_mode rp1vec_modes[4] = {
34 { /* Full size 525/60i with Rec.601 pixel rate */
35 DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
36 - 720, 720 + 14, 720 + 14 + 64, 858, 0,
37 - 480, 480 + 7, 480 + 7 + 6, 525, 0,
38 + 720, 720 + 16, 720 + 16 + 64, 858, 0,
39 + 480, 480 + 6, 480 + 6 + 6, 525, 0,
40 DRM_MODE_FLAG_INTERLACE)
41 },
42 { /* Cropped and horizontally squashed to be TV-safe */
43 DRM_MODE("704x432i", DRM_MODE_TYPE_DRIVER, 15429,
44 - 704, 704 + 72, 704 + 72 + 72, 980, 0,
45 - 432, 432 + 31, 432 + 31 + 6, 525, 0,
46 + 704, 704 + 76, 704 + 76 + 72, 980, 0,
47 + 432, 432 + 30, 432 + 30 + 6, 525, 0,
48 DRM_MODE_FLAG_INTERLACE)
49 },
50 { /* Full size 625/50i with Rec.601 pixel rate */
51 DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
52 720, 720 + 20, 720 + 20 + 64, 864, 0,
53 - 576, 576 + 4, 576 + 4 + 6, 625, 0,
54 + 576, 576 + 5, 576 + 5 + 5, 625, 0,
55 DRM_MODE_FLAG_INTERLACE)
56 },
57 { /* Cropped and squashed, for square(ish) pixels */
58 DRM_MODE("704x512i", DRM_MODE_TYPE_DRIVER, 15429,
59 704, 704 + 80, 704 + 80 + 72, 987, 0,
60 - 512, 512 + 36, 512 + 36 + 6, 625, 0,
61 + 512, 512 + 37, 512 + 37 + 5, 625, 0,
62 DRM_MODE_FLAG_INTERLACE)
63 }
64 };
65 @@ -298,27 +297,42 @@ static enum drm_mode_status rp1vec_mode_
66 const struct drm_display_mode *mode)
67 {
68 /*
69 - * Check the mode roughly matches one of our standard modes
70 - * (optionally half-height and progressive). Ignore H/V sync
71 - * timings which for interlaced TV are approximate at best.
72 + * Check the mode roughly matches something we can generate.
73 + * The hardware driver is very prescriptive about pixel clocks,
74 + * line and frame durations, but we'll tolerate rounding errors.
75 + * Within each hardware mode, allow image size and position to vary
76 + * (to fine-tune overscan correction or emulate retro devices).
77 + * Don't check sync timings here: the HW driver will sanitize them.
78 */
79 - int i, prog;
80
81 - prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
82 -
83 - for (i = 0; i < ARRAY_SIZE(rp1vec_modes); i++) {
84 - const struct drm_display_mode *ref = rp1vec_modes + i;
85 + int prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
86 + int vtotal_full = mode->vtotal << prog;
87 + int vdisplay_full = mode->vdisplay << prog;
88 +
89 + /* Reject very small frames */
90 + if (vtotal_full < 256 || mode->hdisplay < 256)
91 + return MODE_BAD;
92 +
93 + /* Check lines, frame period (ms) and vertical size limit */
94 + if (vtotal_full >= 524 && vtotal_full <= 526 &&
95 + mode->htotal * vtotal_full > 33 * mode->clock &&
96 + mode->htotal * vtotal_full < 34 * mode->clock &&
97 + vdisplay_full <= 480)
98 + goto vgood;
99 + if (vtotal_full >= 624 && vtotal_full <= 626 &&
100 + mode->htotal * vtotal_full > 39 * mode->clock &&
101 + mode->htotal * vtotal_full < 41 * mode->clock &&
102 + vdisplay_full <= 576)
103 + goto vgood;
104 + return MODE_BAD;
105
106 - if (mode->hdisplay == ref->hdisplay &&
107 - mode->vdisplay == (ref->vdisplay >> prog) &&
108 - mode->clock + 2 >= ref->clock &&
109 - mode->clock <= ref->clock + 2 &&
110 - mode->htotal + 2 >= ref->htotal &&
111 - mode->htotal <= ref->htotal + 2 &&
112 - mode->vtotal + 2 >= (ref->vtotal >> prog) &&
113 - mode->vtotal <= (ref->vtotal >> prog) + 2)
114 - return MODE_OK;
115 - }
116 +vgood:
117 + /* Check pixel rate (kHz) and horizontal size limit */
118 + if (mode->clock == 13500 && mode->hdisplay <= 720)
119 + return MODE_OK;
120 + if (mode->clock >= 15428 && mode->clock <= 15429 &&
121 + mode->hdisplay <= 800)
122 + return MODE_OK;
123 return MODE_BAD;
124 }
125
126 @@ -440,7 +454,7 @@ static int rp1vec_platform_probe(struct
127 ret = drmm_mode_config_init(drm);
128 if (ret)
129 goto err_free_drm;
130 - drm->mode_config.max_width = 768;
131 + drm->mode_config.max_width = 800;
132 drm->mode_config.max_height = 576;
133 drm->mode_config.fb_base = 0;
134 drm->mode_config.preferred_depth = 32;
135 --- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
136 +++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
137 @@ -11,7 +11,6 @@
138 #include <linux/delay.h>
139 #include <linux/interrupt.h>
140 #include <linux/platform_device.h>
141 -#include <linux/printk.h>
142 #include <drm/drm_fourcc.h>
143 #include <drm/drm_print.h>
144 #include <drm/drm_vblank.h>
145 @@ -82,13 +81,13 @@ static const struct rp1vec_ipixfmt my_fo
146 /*
147 * Hardware mode descriptions (@ 108 MHz clock rate).
148 * These rely largely on "canned" register settings.
149 - * TODO: Port the generating software from FP to integer,
150 - * or better factorize the differences between modes.
151 */
152
153 struct rp1vec_hwmode {
154 - u16 total_cols; /* active columns, plus padding for filter context */
155 + u16 total_cols; /* max active columns incl. padding and windowing */
156 u16 rows_per_field; /* active lines per field (including partial ones) */
157 + u16 ref_hfp; /* nominal (hsync_start - hdisplay) when max width */
158 + u16 ref_vfp; /* nominal (vsync_start - vdisplay) when max height */
159 bool interlaced; /* set for interlaced */
160 bool first_field_odd; /* set for interlaced and 30fps */
161 u32 yuv_scaling; /* three 10-bit fields {Y, U, V} in 2.8 format */
162 @@ -103,6 +102,8 @@ static const struct rp1vec_hwmode rp1vec
163 {
164 .total_cols = 724,
165 .rows_per_field = 240,
166 + .ref_hfp = 12,
167 + .ref_vfp = 2,
168 .interlaced = false,
169 .first_field_odd = false,
170 .yuv_scaling = 0x1071d0cf,
171 @@ -118,6 +119,8 @@ static const struct rp1vec_hwmode rp1vec
172 }, {
173 .total_cols = 815,
174 .rows_per_field = 240,
175 + .ref_hfp = 16,
176 + .ref_vfp = 2,
177 .interlaced = false,
178 .first_field_odd = false,
179 .yuv_scaling = 0x1c131962,
180 @@ -135,6 +138,8 @@ static const struct rp1vec_hwmode rp1vec
181 {
182 .total_cols = 724,
183 .rows_per_field = 243,
184 + .ref_hfp = 12,
185 + .ref_vfp = 3,
186 .interlaced = true,
187 .first_field_odd = true,
188 .yuv_scaling = 0x1071d0cf,
189 @@ -150,6 +155,8 @@ static const struct rp1vec_hwmode rp1vec
190 }, {
191 .total_cols = 815,
192 .rows_per_field = 243,
193 + .ref_hfp = 16,
194 + .ref_vfp = 3,
195 .interlaced = true,
196 .first_field_odd = true,
197 .yuv_scaling = 0x1c131962,
198 @@ -170,6 +177,8 @@ static const struct rp1vec_hwmode rp1vec
199 {
200 .total_cols = 724,
201 .rows_per_field = 288,
202 + .ref_hfp = 16,
203 + .ref_vfp = 2,
204 .interlaced = false,
205 .first_field_odd = false,
206 .yuv_scaling = 0x11c1f8e0,
207 @@ -185,6 +194,8 @@ static const struct rp1vec_hwmode rp1vec
208 }, {
209 .total_cols = 804,
210 .rows_per_field = 288,
211 + .ref_hfp = 24,
212 + .ref_vfp = 2,
213 .interlaced = false,
214 .first_field_odd = false,
215 .yuv_scaling = 0x1e635d7f,
216 @@ -202,6 +213,8 @@ static const struct rp1vec_hwmode rp1vec
217 {
218 .total_cols = 724,
219 .rows_per_field = 288,
220 + .ref_hfp = 16,
221 + .ref_vfp = 5,
222 .interlaced = true,
223 .first_field_odd = false,
224 .yuv_scaling = 0x11c1f8e0,
225 @@ -217,6 +230,8 @@ static const struct rp1vec_hwmode rp1vec
226 }, {
227 .total_cols = 804,
228 .rows_per_field = 288,
229 + .ref_hfp = 24,
230 + .ref_vfp = 5,
231 .interlaced = true,
232 .first_field_odd = false,
233 .yuv_scaling = 0x1e635d7f,
234 @@ -237,6 +252,8 @@ static const struct rp1vec_hwmode rp1vec
235 {
236 .total_cols = 724,
237 .rows_per_field = 240,
238 + .ref_hfp = 12,
239 + .ref_vfp = 2,
240 .interlaced = false,
241 .first_field_odd = false,
242 .yuv_scaling = 0x11c1f8e0,
243 @@ -252,6 +269,8 @@ static const struct rp1vec_hwmode rp1vec
244 }, {
245 .total_cols = 815,
246 .rows_per_field = 240,
247 + .ref_hfp = 16,
248 + .ref_vfp = 2,
249 .interlaced = false,
250 .first_field_odd = false,
251 .yuv_scaling = 0x1e635d7f,
252 @@ -269,6 +288,8 @@ static const struct rp1vec_hwmode rp1vec
253 {
254 .total_cols = 724,
255 .rows_per_field = 243,
256 + .ref_hfp = 12,
257 + .ref_vfp = 3,
258 .interlaced = true,
259 .first_field_odd = true,
260 .yuv_scaling = 0x11c1f8e0,
261 @@ -284,6 +305,8 @@ static const struct rp1vec_hwmode rp1vec
262 }, {
263 .total_cols = 815,
264 .rows_per_field = 243,
265 + .ref_hfp = 16,
266 + .ref_vfp = 3,
267 .interlaced = true,
268 .first_field_odd = true,
269 .yuv_scaling = 0x1e635d7f,
270 @@ -308,11 +331,11 @@ void rp1vec_hw_setup(struct rp1_vec *vec
271 {
272 unsigned int i, mode_family, mode_ilaced, mode_narrow;
273 const struct rp1vec_hwmode *hwm;
274 - unsigned int w, h;
275 + int w, h, hpad, vpad;
276
277 /* Pick the appropriate "base" mode, which we may modify */
278 mode_ilaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
279 - if (mode->vtotal > 263 * (1 + mode_ilaced))
280 + if (mode->vtotal >= 272 * (1 + mode_ilaced))
281 mode_family = 1;
282 else
283 mode_family = (tvstd == RP1VEC_TVSTD_PAL_M || tvstd == RP1VEC_TVSTD_PAL60) ? 2 : 0;
284 @@ -326,13 +349,28 @@ void rp1vec_hw_setup(struct rp1_vec *vec
285 tvstd, rp1vec_tvstd_names[tvstd]);
286
287 w = mode->hdisplay;
288 - h = mode->vdisplay;
289 - if (mode_ilaced)
290 - h >>= 1;
291 + h = mode->vdisplay >> mode_ilaced;
292 if (w > hwm->total_cols)
293 w = hwm->total_cols;
294 if (h > hwm->rows_per_field)
295 - w = hwm->rows_per_field;
296 + h = hwm->rows_per_field;
297 +
298 + /*
299 + * Add padding so a framebuffer with the given dimensions and
300 + * [hv]sync_start can be displayed in the chosen hardware mode.
301 + *
302 + * |<----- mode->hsync_start ----->|
303 + * |<------ w ------>| |
304 + * | | >|--|< ref_hfp
305 + * |<- hpad ->|
306 + * |<------------ total_cols ----------->|
307 + * ________FRAMEBUFFERCONTENTS__________
308 + * ' `--\____/-<\/\/\>-'
309 + */
310 + hpad = max(0, mode->hsync_start - hwm->ref_hfp - w);
311 + hpad = min(hpad, hwm->total_cols - w);
312 + vpad = max(0, ((mode->vsync_start - hwm->ref_vfp) >> mode_ilaced) - h);
313 + vpad = min(vpad, hwm->rows_per_field - h);
314
315 /* Configure the hardware */
316 VEC_WRITE(VEC_APB_TIMEOUT, 0x38);
317 @@ -347,18 +385,18 @@ void rp1vec_hw_setup(struct rp1_vec *vec
318 BITS(VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1, h - 1));
319 VEC_WRITE(VEC_YUV_SCALING, hwm->yuv_scaling);
320 VEC_WRITE(VEC_BACK_PORCH,
321 - BITS(VEC_BACK_PORCH_HBP_MINUS1, (hwm->total_cols - w - 1) >> 1) |
322 - BITS(VEC_BACK_PORCH_VBP_MINUS1, (hwm->rows_per_field - h - 1) >> 1));
323 + BITS(VEC_BACK_PORCH_HBP_MINUS1, hwm->total_cols - w - hpad - 1) |
324 + BITS(VEC_BACK_PORCH_VBP_MINUS1, hwm->rows_per_field - h - vpad - 1));
325 VEC_WRITE(VEC_FRONT_PORCH,
326 - BITS(VEC_FRONT_PORCH_HFP_MINUS1, (hwm->total_cols - w - 2) >> 1) |
327 - BITS(VEC_FRONT_PORCH_VFP_MINUS1, (hwm->rows_per_field - h - 2) >> 1));
328 + BITS(VEC_FRONT_PORCH_HFP_MINUS1, hpad - 1) |
329 + BITS(VEC_FRONT_PORCH_VFP_MINUS1, vpad - 1));
330 VEC_WRITE(VEC_MODE,
331 BITS(VEC_MODE_HIGH_WATER, 0xE0) |
332 BITS(VEC_MODE_ALIGN16, !((w | mode->hdisplay) & 15)) |
333 - BITS(VEC_MODE_VFP_EN, (hwm->rows_per_field > h + 1)) |
334 - BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h)) |
335 - BITS(VEC_MODE_HFP_EN, (hwm->total_cols > w + 1)) |
336 - BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w)) |
337 + BITS(VEC_MODE_VFP_EN, (vpad > 0)) |
338 + BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h + vpad)) |
339 + BITS(VEC_MODE_HFP_EN, (hpad > 0)) |
340 + BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w + hpad)) |
341 BITS(VEC_MODE_FIELDS_PER_FRAME_MINUS1, hwm->interlaced) |
342 BITS(VEC_MODE_FIRST_FIELD_ODD, hwm->first_field_odd));
343 for (i = 0; i < ARRAY_SIZE(hwm->back_end_regs); ++i) {