bbaee270c52e683ef180998da3777a0f65ed854c
[openwrt/staging/nbd.git] /
1 From 7735dd0736322cff23aff95490bae1d69937a9bf Mon Sep 17 00:00:00 2001
2 From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
3 Date: Tue, 10 Dec 2024 13:23:09 +0000
4 Subject: [PATCH 1456/1482] drm: rp1: rp1-dpi: Add interlaced modes and PIO
5 program to fix VSYNC
6
7 Implement interlaced modes by wobbling the base pointer and VFP width
8 for every field. This results in correct pixels but incorrect VSYNC.
9
10 Now use PIO to generate a fixed-up VSYNC by sampling DE and HSYNC.
11 This requires DPI's DE output to be mapped to GPIO1, which we check.
12
13 When DE is not exposed, the internal fixup is disabled. VSYNC/GPIO2
14 becomes a modified signal, designed to help an external device or
15 PIO program synthesize CSYNC or VSYNC.
16
17 Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
18 ---
19 drivers/gpu/drm/rp1/rp1-dpi/Makefile | 2 +-
20 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 34 ++-
21 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h | 18 ++
22 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c | 253 ++++++++++++++++------
23 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c | 225 +++++++++++++++++++
24 5 files changed, 461 insertions(+), 71 deletions(-)
25 create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c
26
27 --- a/drivers/gpu/drm/rp1/rp1-dpi/Makefile
28 +++ b/drivers/gpu/drm/rp1/rp1-dpi/Makefile
29 @@ -1,5 +1,5 @@
30 # SPDX-License-Identifier: GPL-2.0-only
31
32 -drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o
33 +drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o rp1_dpi_pio.o
34
35 obj-$(CONFIG_DRM_RP1_DPI) += drm-rp1-dpi.o
36 --- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
37 +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
38 @@ -80,6 +80,7 @@ static void rp1dpi_pipe_update(struct dr
39 if (dpi->dpi_running &&
40 fb->format->format != dpi->cur_fmt) {
41 rp1dpi_hw_stop(dpi);
42 + rp1dpi_pio_stop(dpi);
43 dpi->dpi_running = false;
44 }
45 if (!dpi->dpi_running) {
46 @@ -88,6 +89,7 @@ static void rp1dpi_pipe_update(struct dr
47 dpi->bus_fmt,
48 dpi->de_inv,
49 &pipe->crtc.state->mode);
50 + rp1dpi_pio_start(dpi, &pipe->crtc.state->mode);
51 dpi->dpi_running = true;
52 }
53 dpi->cur_fmt = fb->format->format;
54 @@ -187,6 +189,7 @@ static void rp1dpi_pipe_disable(struct d
55 drm_crtc_vblank_off(&pipe->crtc);
56 if (dpi->dpi_running) {
57 rp1dpi_hw_stop(dpi);
58 + rp1dpi_pio_stop(dpi);
59 dpi->dpi_running = false;
60 }
61 clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
62 @@ -236,6 +239,7 @@ static void rp1dpi_stopall(struct drm_de
63 if (dpi->dpi_running || rp1dpi_hw_busy(dpi)) {
64 rp1dpi_hw_stop(dpi);
65 clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
66 + rp1dpi_pio_stop(dpi);
67 dpi->dpi_running = false;
68 }
69 rp1dpi_vidout_poweroff(dpi);
70 @@ -273,7 +277,7 @@ static int rp1dpi_platform_probe(struct
71 struct rp1_dpi *dpi;
72 struct drm_bridge *bridge = NULL;
73 struct drm_panel *panel;
74 - int i, ret;
75 + int i, j, ret;
76
77 dev_info(dev, __func__);
78 ret = drm_of_find_panel_or_bridge(pdev->dev.of_node, 0, 0,
79 @@ -295,6 +299,7 @@ static int rp1dpi_platform_probe(struct
80 return ret;
81 }
82 dpi->pdev = pdev;
83 + spin_lock_init(&dpi->hw_lock);
84
85 dpi->bus_fmt = default_bus_fmt;
86 ret = of_property_read_u32(dev->of_node, "default_bus_fmt", &dpi->bus_fmt);
87 @@ -332,6 +337,33 @@ static int rp1dpi_platform_probe(struct
88 if (ret)
89 goto done_err;
90
91 + /* Check if PIO can snoop on or override DPI's GPIO1 */
92 + dpi->gpio1_used = false;
93 + for (i = 0; !dpi->gpio1_used; i++) {
94 + u32 p = 0;
95 + const char *str = NULL;
96 + struct device_node *np1 = of_parse_phandle(dev->of_node, "pinctrl-0", i);
97 +
98 + if (!np1)
99 + break;
100 +
101 + if (!of_property_read_string(np1, "function", &str) && !strcmp(str, "dpi")) {
102 + for (j = 0; !dpi->gpio1_used; j++) {
103 + if (of_property_read_string_index(np1, "pins", j, &str))
104 + break;
105 + if (!strcmp(str, "gpio1"))
106 + dpi->gpio1_used = true;
107 + }
108 + for (j = 0; !dpi->gpio1_used; j++) {
109 + if (of_property_read_u32_index(np1, "brcm,pins", j, &p))
110 + break;
111 + if (p == 1)
112 + dpi->gpio1_used = true;
113 + }
114 + }
115 + of_node_put(np1);
116 + }
117 +
118 /* Now we have all our resources, finish driver initialization */
119 dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
120 init_completion(&dpi->finished);
121 --- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
122 +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
123 @@ -46,6 +46,18 @@ struct rp1_dpi {
124 bool de_inv, clk_inv;
125 bool dpi_running, pipe_enabled;
126 struct completion finished;
127 +
128 + /* Experimental stuff for interlace follows */
129 + struct rp1_pio_client *pio;
130 + bool gpio1_used;
131 + bool pio_stole_gpio2;
132 +
133 + spinlock_t hw_lock; /* the following are used in line-match ISR */
134 + dma_addr_t last_dma_addr;
135 + u32 last_stride;
136 + u32 shorter_front_porch;
137 + bool interlaced;
138 + bool lower_field_flag;
139 };
140
141 /* ---------------------------------------------------------------------- */
142 @@ -67,3 +79,9 @@ void rp1dpi_hw_vblank_ctrl(struct rp1_dp
143
144 void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge);
145 void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi);
146 +
147 +/* ---------------------------------------------------------------------- */
148 +/* PIO control -- we need PIO to generate VSync (from DE) when interlaced */
149 +
150 +int rp1dpi_pio_start(struct rp1_dpi *dpi, const struct drm_display_mode *mode);
151 +void rp1dpi_pio_stop(struct rp1_dpi *dpi);
152 --- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
153 +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
154 @@ -202,7 +202,7 @@
155 // Status
156 #define DPI_DMA_STATUS 0x3c
157
158 -#define BITS(field, val) (((val) << (field ## _SHIFT)) & (field ## _MASK))
159 +#define BITS(field, val) FIELD_PREP((field ## _MASK), val)
160
161 static unsigned int rp1dpi_hw_read(struct rp1_dpi *dpi, unsigned int reg)
162 {
163 @@ -231,69 +231,73 @@ struct rp1dpi_ipixfmt {
164 u32 rgbsz; /* Shifts used for scaling; also (BPP/8-1) */
165 };
166
167 -#define IMASK_RGB(r, g, b) (BITS(DPI_DMA_IMASK_R, r) | \
168 - BITS(DPI_DMA_IMASK_G, g) | \
169 - BITS(DPI_DMA_IMASK_B, b))
170 -#define OMASK_RGB(r, g, b) (BITS(DPI_DMA_OMASK_R, r) | \
171 - BITS(DPI_DMA_OMASK_G, g) | \
172 - BITS(DPI_DMA_OMASK_B, b))
173 -#define ISHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_IR, r) | \
174 - BITS(DPI_DMA_SHIFT_IG, g) | \
175 - BITS(DPI_DMA_SHIFT_IB, b))
176 -#define OSHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_OR, r) | \
177 - BITS(DPI_DMA_SHIFT_OG, g) | \
178 - BITS(DPI_DMA_SHIFT_OB, b))
179 +#define IMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_IMASK_R_MASK, r) | \
180 + FIELD_PREP_CONST(DPI_DMA_IMASK_G_MASK, g) | \
181 + FIELD_PREP_CONST(DPI_DMA_IMASK_B_MASK, b))
182 +#define OMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_OMASK_R_MASK, r) | \
183 + FIELD_PREP_CONST(DPI_DMA_OMASK_G_MASK, g) | \
184 + FIELD_PREP_CONST(DPI_DMA_OMASK_B_MASK, b))
185 +#define ISHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_IR_MASK, r) | \
186 + FIELD_PREP_CONST(DPI_DMA_SHIFT_IG_MASK, g) | \
187 + FIELD_PREP_CONST(DPI_DMA_SHIFT_IB_MASK, b))
188 +#define OSHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_OR_MASK, r) | \
189 + FIELD_PREP_CONST(DPI_DMA_SHIFT_OG_MASK, g) | \
190 + FIELD_PREP_CONST(DPI_DMA_SHIFT_OB_MASK, b))
191
192 static const struct rp1dpi_ipixfmt my_formats[] = {
193 {
194 .format = DRM_FORMAT_XRGB8888,
195 .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
196 .shift = ISHIFT_RGB(23, 15, 7),
197 - .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
198 + .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3),
199 },
200 {
201 .format = DRM_FORMAT_XBGR8888,
202 .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
203 .shift = ISHIFT_RGB(7, 15, 23),
204 - .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
205 + .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3),
206 },
207 {
208 .format = DRM_FORMAT_ARGB8888,
209 .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
210 .shift = ISHIFT_RGB(23, 15, 7),
211 - .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
212 + .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3),
213 },
214 {
215 .format = DRM_FORMAT_ABGR8888,
216 .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
217 .shift = ISHIFT_RGB(7, 15, 23),
218 - .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
219 + .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3),
220 },
221 {
222 .format = DRM_FORMAT_RGB888,
223 .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
224 .shift = ISHIFT_RGB(23, 15, 7),
225 - .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2),
226 + .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 2),
227 },
228 {
229 .format = DRM_FORMAT_BGR888,
230 .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
231 .shift = ISHIFT_RGB(7, 15, 23),
232 - .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2),
233 + .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 2),
234 },
235 {
236 .format = DRM_FORMAT_RGB565,
237 .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
238 .shift = ISHIFT_RGB(15, 10, 4),
239 - .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
240 - BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
241 + .rgbsz = (FIELD_PREP_CONST(DPI_DMA_RGBSZ_R_MASK, 5) |
242 + FIELD_PREP_CONST(DPI_DMA_RGBSZ_G_MASK, 6) |
243 + FIELD_PREP_CONST(DPI_DMA_RGBSZ_B_MASK, 5) |
244 + FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 1)),
245 },
246 {
247 .format = DRM_FORMAT_BGR565,
248 .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
249 .shift = ISHIFT_RGB(4, 10, 15),
250 - .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
251 - BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
252 + .rgbsz = (FIELD_PREP_CONST(DPI_DMA_RGBSZ_R_MASK, 5) |
253 + FIELD_PREP_CONST(DPI_DMA_RGBSZ_G_MASK, 6) |
254 + FIELD_PREP_CONST(DPI_DMA_RGBSZ_B_MASK, 5) |
255 + FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 1)),
256 }
257 };
258
259 @@ -354,42 +358,26 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi
260 u32 in_format, u32 bus_format, bool de_inv,
261 struct drm_display_mode const *mode)
262 {
263 - u32 shift, imask, omask, rgbsz;
264 + u32 shift, imask, omask, rgbsz, vctrl;
265 int i;
266
267 - pr_info("%s: in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d %dkHz %cH%cV%cD%cC",
268 - __func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format,
269 - mode->hdisplay, mode->vdisplay,
270 - mode->htotal, mode->vtotal,
271 - mode->clock,
272 - (mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+',
273 - (mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+',
274 - de_inv ? '-' : '+',
275 - dpi->clk_inv ? '-' : '+');
276 + drm_info(&dpi->drm,
277 + "in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d%s %dkHz %cH%cV%cD%cC",
278 + in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format,
279 + mode->hdisplay, mode->vdisplay,
280 + mode->htotal, mode->vtotal,
281 + (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "i" : "",
282 + mode->clock,
283 + (mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+',
284 + (mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+',
285 + de_inv ? '-' : '+',
286 + dpi->clk_inv ? '-' : '+');
287
288 /*
289 * Configure all DPI/DMA block registers, except base address.
290 * DMA will not actually start until a FB base address is specified
291 * using rp1dpi_hw_update().
292 */
293 - rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA,
294 - BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) |
295 - BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1));
296 -
297 - rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH,
298 - BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, mode->vsync_end - mode->vsync_start - 1) |
299 - BITS(DPI_DMA_SYNC_WIDTH_COLSM1, mode->hsync_end - mode->hsync_start - 1));
300 -
301 - /* In these registers, "back porch" time includes sync width */
302 - rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH,
303 - BITS(DPI_DMA_BACK_PORCH_ROWSM1, mode->vtotal - mode->vsync_start - 1) |
304 - BITS(DPI_DMA_BACK_PORCH_COLSM1, mode->htotal - mode->hsync_start - 1));
305 -
306 - rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH,
307 - BITS(DPI_DMA_FRONT_PORCH_ROWSM1, mode->vsync_start - mode->vdisplay - 1) |
308 - BITS(DPI_DMA_FRONT_PORCH_COLSM1, mode->hsync_start - mode->hdisplay - 1));
309 -
310 - /* Input to output pixel format conversion */
311 for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
312 if (my_formats[i].format == in_format)
313 break;
314 @@ -417,6 +405,89 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi
315 BITS(DPI_DMA_QOS_LLEV, 0x8) |
316 BITS(DPI_DMA_QOS_LQOS, 0x7));
317
318 + if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) {
319 + rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA,
320 + BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) |
321 + BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1));
322 +
323 + rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH,
324 + BITS(DPI_DMA_SYNC_WIDTH_ROWSM1,
325 + mode->vsync_end - mode->vsync_start - 1) |
326 + BITS(DPI_DMA_SYNC_WIDTH_COLSM1,
327 + mode->hsync_end - mode->hsync_start - 1));
328 +
329 + /* In these registers, "back porch" time includes sync width */
330 + rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH,
331 + BITS(DPI_DMA_BACK_PORCH_ROWSM1,
332 + mode->vtotal - mode->vsync_start - 1) |
333 + BITS(DPI_DMA_BACK_PORCH_COLSM1,
334 + mode->htotal - mode->hsync_start - 1));
335 +
336 + rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH,
337 + BITS(DPI_DMA_FRONT_PORCH_ROWSM1,
338 + mode->vsync_start - mode->vdisplay - 1) |
339 + BITS(DPI_DMA_FRONT_PORCH_COLSM1,
340 + mode->hsync_start - mode->hdisplay - 1));
341 +
342 + vctrl = BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) |
343 + BITS(DPI_DMA_CONTROL_VBP_EN, (mode->vtotal != mode->vsync_start)) |
344 + BITS(DPI_DMA_CONTROL_VFP_EN, (mode->vsync_start != mode->vdisplay)) |
345 + BITS(DPI_DMA_CONTROL_VSYNC_EN, (mode->vsync_end != mode->vsync_start));
346 +
347 + dpi->interlaced = false;
348 + } else {
349 + /*
350 + * Experimental interlace support
351 + *
352 + * RP1 DPI hardware wasn't designed to support interlace, but lets us change
353 + * both the VFP line count and the next DMA address while running. That allows
354 + * pixel data to be correctly timed for interlace, but VSYNC remains wrong.
355 + *
356 + * It is necessary to use external hardware (such as PIO) to regenerate VSYNC
357 + * based on HSYNC, DE (which *must* both be mapped to GPIOs 1, 3 respectively).
358 + * This driver includes a PIO program to do that, when DE is enabled.
359 + *
360 + * An alternative fixup is to synthesize CSYNC from HSYNC and modified-VSYNC.
361 + * We don't implement that here, but to facilitate it, DPI's VSYNC is replaced
362 + * by a "helper signal" that pulses low for 1 or 2 scan-lines, starting 2.0 or
363 + * 2.5 scan-lines respectively before nominal VSYNC start.
364 + */
365 + int vact = mode->vdisplay >> 1; /* visible lines per field. Can't do half-lines */
366 + int vtot0 = mode->vtotal >> 1; /* vtotal should always be odd when interlaced. */
367 + int vfp0 = (mode->vsync_start >= mode->vdisplay + 4) ?
368 + ((mode->vsync_start - mode->vdisplay - 2) >> 1) : 1;
369 + int vbp = max(0, vtot0 - vact - vfp0);
370 +
371 + rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA,
372 + BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, vact - 1) |
373 + BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1));
374 +
375 + rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH,
376 + BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, vtot0 - 2) |
377 + BITS(DPI_DMA_SYNC_WIDTH_COLSM1,
378 + mode->hsync_end - mode->hsync_start - 1));
379 +
380 + rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH,
381 + BITS(DPI_DMA_BACK_PORCH_ROWSM1, vbp - 1) |
382 + BITS(DPI_DMA_BACK_PORCH_COLSM1,
383 + mode->htotal - mode->hsync_start - 1));
384 +
385 + dpi->shorter_front_porch =
386 + BITS(DPI_DMA_FRONT_PORCH_ROWSM1, vfp0 - 1) |
387 + BITS(DPI_DMA_FRONT_PORCH_COLSM1,
388 + mode->hsync_start - mode->hdisplay - 1);
389 + rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH, dpi->shorter_front_porch);
390 +
391 + vctrl = BITS(DPI_DMA_CONTROL_VSYNC_POL, 0) |
392 + BITS(DPI_DMA_CONTROL_VBP_EN, (vbp > 0)) |
393 + BITS(DPI_DMA_CONTROL_VFP_EN, 1) |
394 + BITS(DPI_DMA_CONTROL_VSYNC_EN, 1);
395 +
396 + dpi->interlaced = true;
397 + }
398 + dpi->lower_field_flag = false;
399 + dpi->last_dma_addr = 0;
400 +
401 rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, -1);
402 rp1dpi_hw_vblank_ctrl(dpi, 1);
403
404 @@ -425,49 +496,64 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi
405 pr_warn("%s: Unexpectedly busy at start!", __func__);
406
407 rp1dpi_hw_write(dpi, DPI_DMA_CONTROL,
408 + vctrl |
409 BITS(DPI_DMA_CONTROL_ARM, !i) |
410 BITS(DPI_DMA_CONTROL_AUTO_REPEAT, 1) |
411 BITS(DPI_DMA_CONTROL_HIGH_WATER, 448) |
412 BITS(DPI_DMA_CONTROL_DEN_POL, de_inv) |
413 BITS(DPI_DMA_CONTROL_HSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NHSYNC)) |
414 - BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) |
415 - BITS(DPI_DMA_CONTROL_COLORM, 0) |
416 - BITS(DPI_DMA_CONTROL_SHUTDN, 0) |
417 BITS(DPI_DMA_CONTROL_HBP_EN, (mode->htotal != mode->hsync_end)) |
418 BITS(DPI_DMA_CONTROL_HFP_EN, (mode->hsync_start != mode->hdisplay)) |
419 - BITS(DPI_DMA_CONTROL_VBP_EN, (mode->vtotal != mode->vsync_end)) |
420 - BITS(DPI_DMA_CONTROL_VFP_EN, (mode->vsync_start != mode->vdisplay)) |
421 - BITS(DPI_DMA_CONTROL_HSYNC_EN, (mode->hsync_end != mode->hsync_start)) |
422 - BITS(DPI_DMA_CONTROL_VSYNC_EN, (mode->vsync_end != mode->vsync_start)));
423 + BITS(DPI_DMA_CONTROL_HSYNC_EN, (mode->hsync_end != mode->hsync_start)));
424 }
425
426 void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride)
427 {
428 - u64 a = addr + offset;
429 + unsigned long flags;
430 +
431 + spin_lock_irqsave(&dpi->hw_lock, flags);
432
433 /*
434 * Update STRIDE, DMAH and DMAL only. When called after rp1dpi_hw_setup(),
435 * DMA starts immediately; if already running, the buffer will flip at
436 - * the next vertical sync event.
437 + * the next vertical sync event. In interlaced mode, we need to adjust
438 + * the address and stride to display only the current field, saving
439 + * the original address (so it can be flipped for subsequent fields).
440 */
441 + addr += offset;
442 + dpi->last_dma_addr = addr;
443 + dpi->last_stride = stride;
444 + if (dpi->interlaced) {
445 + if (dpi->lower_field_flag)
446 + addr += stride;
447 + stride *= 2;
448 + }
449 rp1dpi_hw_write(dpi, DPI_DMA_DMA_STRIDE, stride);
450 - rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32);
451 - rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu);
452 + rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, addr >> 32);
453 + rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, addr & 0xFFFFFFFFu);
454 +
455 + spin_unlock_irqrestore(&dpi->hw_lock, flags);
456 }
457
458 void rp1dpi_hw_stop(struct rp1_dpi *dpi)
459 {
460 u32 ctrl;
461 + unsigned long flags;
462
463 /*
464 - * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
465 - * the current and any queued frame to end. "Force drain" flags are not used,
466 - * as they seem to prevent DMA from re-starting properly; it's safer to wait.
467 + * Stop DMA by turning off Auto-Repeat (and disable S/W field-flip),
468 + * then wait up to 100ms for the current and any queued frame to end.
469 + * (There is a "force drain" flag, but it can leave DPI in a broken
470 + * state which prevents it from restarting; it's safer to wait.)
471 */
472 + spin_lock_irqsave(&dpi->hw_lock, flags);
473 + dpi->last_dma_addr = 0;
474 reinit_completion(&dpi->finished);
475 ctrl = rp1dpi_hw_read(dpi, DPI_DMA_CONTROL);
476 ctrl &= ~(DPI_DMA_CONTROL_ARM_MASK | DPI_DMA_CONTROL_AUTO_REPEAT_MASK);
477 rp1dpi_hw_write(dpi, DPI_DMA_CONTROL, ctrl);
478 + spin_unlock_irqrestore(&dpi->hw_lock, flags);
479 +
480 if (!wait_for_completion_timeout(&dpi->finished, HZ / 10))
481 drm_err(&dpi->drm, "%s: timed out waiting for idle\n", __func__);
482 rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN, 0);
483 @@ -476,10 +562,11 @@ void rp1dpi_hw_stop(struct rp1_dpi *dpi)
484 void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable)
485 {
486 rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN,
487 - BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1) |
488 - BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1) |
489 - BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) |
490 - BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 4095));
491 + BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1) |
492 + BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1) |
493 + BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) |
494 + BITS(DPI_DMA_IRQ_EN_MATCH, dpi->interlaced) |
495 + BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 32));
496 }
497
498 irqreturn_t rp1dpi_hw_isr(int irq, void *dev)
499 @@ -498,7 +585,35 @@ irqreturn_t rp1dpi_hw_isr(int irq, void
500 drm_crtc_handle_vblank(&dpi->pipe.crtc);
501 if (u & DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK)
502 complete(&dpi->finished);
503 +
504 + /*
505 + * Added for interlace support: We use this mid-frame interrupt to
506 + * wobble the VFP between fields, re-submitting the next-buffer address
507 + * with an offset to display the opposite field. NB: rp1dpi_hw_update()
508 + * may be called at any time, before or after, so locking is needed.
509 + * H/W Auto-update is no longer needed (unless this IRQ is lost).
510 + */
511 + if ((u & DPI_DMA_IRQ_FLAGS_MATCH_MASK) && dpi->interlaced) {
512 + unsigned long flags;
513 + dma_addr_t a;
514 +
515 + spin_lock_irqsave(&dpi->hw_lock, flags);
516 + dpi->lower_field_flag = !dpi->lower_field_flag;
517 + rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH,
518 + dpi->shorter_front_porch +
519 + BITS(DPI_DMA_FRONT_PORCH_ROWSM1,
520 + dpi->lower_field_flag));
521 + a = dpi->last_dma_addr;
522 + if (a) {
523 + if (dpi->lower_field_flag)
524 + a += dpi->last_stride;
525 + rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32);
526 + rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu);
527 + }
528 + spin_unlock_irqrestore(&dpi->hw_lock, flags);
529 + }
530 }
531 }
532 +
533 return u ? IRQ_HANDLED : IRQ_NONE;
534 }
535 --- /dev/null
536 +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_pio.c
537 @@ -0,0 +1,225 @@
538 +// SPDX-License-Identifier: GPL-2.0-only
539 +/*
540 + * PIO code for Raspberry Pi RP1 DPI driver
541 + *
542 + * Copyright (c) 2024 Raspberry Pi Limited.
543 + */
544 +
545 +/*
546 + * Use PIO to fix up VSYNC for interlaced modes.
547 + *
548 + * For this to work we *require* DPI's pinctrl to enable DE on GPIO1.
549 + * PIO can then snoop on HSYNC and DE pins to generate corrected VSYNC.
550 + *
551 + * Note that corrected VSYNC outputs will not be synchronous to DPICLK,
552 + * will lag HSYNC by about 30ns and may suffer up to 5ns of jitter.
553 + */
554 +
555 +#include <linux/kernel.h>
556 +#include <linux/errno.h>
557 +#include <linux/of.h>
558 +#include <linux/pio_rp1.h>
559 +#include <linux/pinctrl/consumer.h>
560 +#include <linux/platform_device.h>
561 +#include <drm/drm_print.h>
562 +
563 +#include "rp1_dpi.h"
564 +
565 +/*
566 + * Start a PIO SM to generate an interrupt just after HSYNC onset, then another
567 + * after a fixed delay (during which we assume HSYNC will have been deasserted).
568 + */
569 +
570 +static int rp1dpi_pio_start_timer_both(struct rp1_dpi *dpi, u32 flags, u32 tc)
571 +{
572 + static const u16 instructions[2][5] = {
573 + { 0xa022, 0x2083, 0xc001, 0x0043, 0xc001 }, /* posedge */
574 + { 0xa022, 0x2003, 0xc001, 0x0043, 0xc001 }, /* negedge */
575 + };
576 + const struct pio_program prog = {
577 + .instructions = instructions[(flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0],
578 + .length = ARRAY_SIZE(instructions[0]),
579 + .origin = -1
580 + };
581 + int offset, sm;
582 +
583 + sm = pio_claim_unused_sm(dpi->pio, true);
584 + if (sm < 0)
585 + return -EBUSY;
586 +
587 + offset = pio_add_program(dpi->pio, &prog);
588 + if (offset == PIO_ORIGIN_ANY)
589 + return -EBUSY;
590 +
591 + pio_sm_config cfg = pio_get_default_sm_config();
592 +
593 + pio_sm_set_enabled(dpi->pio, sm, false);
594 + sm_config_set_wrap(&cfg, offset, offset + 4);
595 + pio_sm_init(dpi->pio, sm, offset, &cfg);
596 +
597 + pio_sm_put(dpi->pio, sm, tc - 4);
598 + pio_sm_exec(dpi->pio, sm, pio_encode_pull(false, false));
599 + pio_sm_exec(dpi->pio, sm, pio_encode_out(pio_y, 32));
600 + pio_sm_set_enabled(dpi->pio, sm, true);
601 +
602 + return 0;
603 +}
604 +
605 +/*
606 + * Snoop on DE, HSYNC to count half-lines in the vertical blanking interval
607 + * to determine when the VSYNC pulse should start and finish. Then, at a
608 + * suitable moment (which should be an odd number of half-lines since the
609 + * last active line), sample DE again to detect field phase.
610 + *
611 + * This version assumes VFP length is within 2..129 half-lines for any field
612 + * (one half-line delay is needed to sample DE; we always wait for the next
613 + * half-line boundary to improve VSync start accuracy).
614 + */
615 +
616 +static int rp1dpi_pio_vsync_ilace(struct rp1_dpi *dpi,
617 + struct drm_display_mode const *mode)
618 +{
619 + static const int wrap_target = 14;
620 + static const int wrap = 26;
621 + u16 instructions[] = { /* This is mutable */
622 + 0xa0e6, // 0: mov osr, isr side 0 ; top: rewind parameters
623 + 0x2081, // 1: wait 1 gpio, 1 side 0 ; main: while (!DE) wait;
624 + 0x2783, // 2: wait 1 gpio, 3 side 0 [7] ; do { @HSync
625 + 0xc041, // 3: irq clear 1 side 0 ; flush stale IRQs
626 + 0x20c1, // 4: wait 1 irq, 1 side 0 ; @midline
627 + 0x00c1, // 5: jmp pin, 1 side 0 ; } while (DE)
628 + 0x0007, // 6: jmp 7 side 0 ; <modify for -DE fixup>
629 + 0x6027, // 7: out x, 7 side 0 ; x = VFPlen - 2
630 + 0x000a, // 8: jmp 10 side 0 ; while (x--) {
631 + 0x20c1, // 9: wait 1 irq, 1 side 0 ; @halfline
632 + 0x0049, // 10: jmp x--, 9 side 0 ; }
633 + 0x6021, // 11: out x, 1 side 0 ; test for aligned case
634 + 0x003a, // 12: jmp !x, 26 side 0 ; if (!x) goto precise;
635 + 0x20c1, // 13: wait 1 irq, 1 side 0 ; @halfline
636 + // .wrap_target ; vsjoin:
637 + 0xb722, // 14: mov x, y side 1 [7] ; VSYNC=1; x = VSyncLen
638 + 0xd041, // 15: irq clear 1 side 1 ; VSYNC=1; flush stale IRQs
639 + 0x30c1, // 16: wait 1 irq, 1 side 1 ; VSYNC=1; do { @halfline
640 + 0x1050, // 17: jmp x--, 16 side 1 ; VSYNC=1; } while (x--)
641 + 0x6028, // 18: out x, 8 side 0 ; VSYNC=0; x = VBPLen
642 + 0x0015, // 19: jmp 21 side 0 ; while (x--) {
643 + 0x20c1, // 20: wait 1 irq, 1 side 0 ; @halfline
644 + 0x0054, // 21: jmp x--, 20 side 0 ; }
645 + 0x00c0, // 22: jmp pin, 0 side 0 ; if (DE) reset phase
646 + 0x0018, // 23: jmp 24 side 0 ; <modify for -DE fixup>
647 + 0x00e1, // 24: jmp !osre, 1 side 0 ; if (!phase) goto main
648 + 0x0000, // 25: jmp 0 side 0 ; goto top
649 + 0x2083, // 26: wait 1 gpio, 3 side 0 ; precise: @HSync
650 + // .wrap ; goto vsjoin
651 + };
652 + struct pio_program prog = {
653 + .instructions = instructions,
654 + .length = ARRAY_SIZE(instructions),
655 + .origin = -1
656 + };
657 + pio_sm_config cfg = pio_get_default_sm_config();
658 + unsigned int i, offset;
659 + u32 tc, vfp, vbp;
660 + u32 sysclk = clock_get_hz(clk_sys);
661 + int sm = pio_claim_unused_sm(dpi->pio, true);
662 +
663 + if (sm < 0)
664 + return -EBUSY;
665 +
666 + /* Compute mid-line time constant and start the timer SM */
667 + tc = (mode->htotal * (u64)sysclk) / (u64)(2000u * mode->clock);
668 + if (rp1dpi_pio_start_timer_both(dpi, mode->flags, tc) < 0) {
669 + pio_sm_unclaim(dpi->pio, sm);
670 + return -EBUSY;
671 + }
672 +
673 + /* Adapt program code according to DE and Sync polarity; configure program */
674 + pio_sm_set_enabled(dpi->pio, sm, false);
675 + if (dpi->de_inv) {
676 + instructions[1] ^= 0x0080;
677 + instructions[5] = 0x00c7;
678 + instructions[6] = 0x0001;
679 + instructions[22] = 0x00d8;
680 + instructions[23] = 0x0000;
681 + }
682 + for (i = 0; i < ARRAY_SIZE(instructions); i++) {
683 + if (mode->flags & DRM_MODE_FLAG_NVSYNC)
684 + instructions[i] ^= 0x1000;
685 + if ((mode->flags & DRM_MODE_FLAG_NHSYNC) && (instructions[i] & 0xe07f) == 0x2003)
686 + instructions[i] ^= 0x0080;
687 + }
688 + offset = pio_add_program(dpi->pio, &prog);
689 + if (offset == PIO_ORIGIN_ANY)
690 + return -EBUSY;
691 +
692 + /* Configure pins and SM */
693 + dpi->pio_stole_gpio2 = true;
694 + sm_config_set_wrap(&cfg, offset + wrap_target, offset + wrap);
695 + sm_config_set_sideset(&cfg, 1, false, false);
696 + sm_config_set_sideset_pins(&cfg, 2);
697 + pio_gpio_init(dpi->pio, 2);
698 + sm_config_set_jmp_pin(&cfg, 1); /* "DE" is always GPIO1 */
699 + pio_sm_init(dpi->pio, sm, offset, &cfg);
700 + pio_sm_set_consecutive_pindirs(dpi->pio, sm, 2, 1, true);
701 +
702 + /* Compute vertical times, remembering how we rounded vdisplay, vtotal */
703 + vfp = mode->vsync_start - (mode->vdisplay & ~1);
704 + vbp = (mode->vtotal | 1) - mode->vsync_end;
705 + if (vfp > 128) {
706 + vbp += vfp - 128;
707 + vfp = 128;
708 + } else if (vfp < 3) {
709 + vbp = (vbp > 3 - vfp) ? (vbp - 3 + vfp) : 0;
710 + vfp = 3;
711 + }
712 +
713 + pio_sm_put(dpi->pio, sm,
714 + (vfp - 2) + ((vfp & 1) << 7) + (vbp << 8) +
715 + ((vfp - 3) << 16) + (((~vfp) & 1) << 23) + ((vbp + 1) << 24));
716 + pio_sm_put(dpi->pio, sm, mode->vsync_end - mode->vsync_start - 1);
717 + pio_sm_exec(dpi->pio, sm, pio_encode_pull(false, false));
718 + pio_sm_exec(dpi->pio, sm, pio_encode_out(pio_y, 32));
719 + pio_sm_exec(dpi->pio, sm, pio_encode_in(pio_y, 32));
720 + pio_sm_exec(dpi->pio, sm, pio_encode_pull(false, false));
721 + pio_sm_exec(dpi->pio, sm, pio_encode_out(pio_y, 32));
722 + pio_sm_set_enabled(dpi->pio, sm, true);
723 +
724 + return 0;
725 +}
726 +
727 +int rp1dpi_pio_start(struct rp1_dpi *dpi, const struct drm_display_mode *mode)
728 +{
729 + int r;
730 +
731 + if (!(mode->flags & DRM_MODE_FLAG_INTERLACE) || !dpi->gpio1_used)
732 + return 0;
733 +
734 + if (dpi->pio)
735 + pio_close(dpi->pio);
736 +
737 + dpi->pio = pio_open();
738 + if (IS_ERR(dpi->pio)) {
739 + drm_err(&dpi->drm, "Could not open PIO\n");
740 + dpi->pio = NULL;
741 + return -ENODEV;
742 + }
743 +
744 + r = rp1dpi_pio_vsync_ilace(dpi, mode);
745 + if (r) {
746 + drm_err(&dpi->drm, "Failed to initialize PIO\n");
747 + rp1dpi_pio_stop(dpi);
748 + }
749 +
750 + return r;
751 +}
752 +
753 +void rp1dpi_pio_stop(struct rp1_dpi *dpi)
754 +{
755 + if (dpi->pio) {
756 + if (dpi->pio_stole_gpio2)
757 + pio_gpio_set_function(dpi->pio, 2, GPIO_FUNC_FSEL1);
758 + pio_close(dpi->pio);
759 + dpi->pio_stole_gpio2 = false;
760 + dpi->pio = NULL;
761 + }
762 +}