Skip to content

Commit a76b7b2

Browse files
drm: rp1: rp1-vec: Support 60fps in interlaced modes; other tweaks
To work around the 30fps buffer-flip rate limit when using VEC's "native" interlaced modes, switch to sending individual fields to the VEC BE, using an ISR to flip between fields. When the TV mode is NTSC, change advertised progressive modes to have 263 total lines; this ameliorates colour artifacts, although it reduces the frame rate slightly from 60.05Hz to 59.83Hz. Progressive modes with 262 lines remain supported. Fix an error in equalising pulse configuration for PAL-M/PAL60. Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
1 parent 8dc3412 commit a76b7b2

File tree

3 files changed

+91
-28
lines changed

3 files changed

+91
-28
lines changed

drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,9 @@ static int rp1vec_connector_get_modes(struct drm_connector *connector)
358358
mode->vdisplay >>= 1;
359359
mode->vsync_start >>= 1;
360360
mode->vsync_end >>= 1;
361-
mode->vtotal >>= 1;
361+
mode->vtotal >>= 1;
362+
if (mode->vtotal == 262 && tvstd < DRM_MODE_TV_MODE_PAL)
363+
mode->vtotal++;
362364
} else if (mode->hdisplay == 704 && mode->vtotal == preferred_lines) {
363365
mode->type |= DRM_MODE_TYPE_PREFERRED;
364366
}
@@ -468,6 +470,7 @@ static int rp1vec_platform_probe(struct platform_device *pdev)
468470
return ret;
469471
}
470472
vec->pdev = pdev;
473+
spin_lock_init(&vec->hw_lock);
471474

472475
for (i = 0; i < RP1VEC_NUM_HW_BLOCKS; i++) {
473476
vec->hw_base[i] =

drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ struct rp1_vec {
5050
u32 cur_fmt;
5151
bool fake_31khz, vec_running, pipe_enabled;
5252
struct completion finished;
53+
54+
spinlock_t hw_lock; /* the following are used in line-match ISR */
55+
dma_addr_t last_dma_addr;
56+
u32 last_stride;
57+
bool field_flip;
58+
bool lower_field_flag;
5359
};
5460

5561
/* ---------------------------------------------------------------------- */

drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c

Lines changed: 81 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,18 @@ static const struct rp1vec_ipixfmt my_formats[] = {
102102
* See "vec_regs.h" for further descriptions of these registers and fields.
103103
* Driver should adjust some values for other TV standards and for pixel rate,
104104
* and must ensure that ((de_end - de_bgn) % rate) == 0.
105+
*
106+
* To support 60fps update in interlaced modes, we now do ISR-based field-flip.
107+
* The FIELDS_PER_FRAME_MINUS1 flag in "misc" is no longer set. Some vertical
108+
* timings have been rotated wrt conventional line-numbering (to ensure a gap
109+
* between the last active line and nominal end-of-field).
105110
*/
106111

107112
struct rp1vec_hwmode {
108113
u16 max_rows_per_field; /* active lines per field (including partial ones) */
109114
u16 ref_vfp; /* nominal (vsync_start - vdisplay) when max height */
110115
bool interlaced; /* set for interlaced */
111-
bool first_field_odd; /* depends confusingly on line numbering convention */
116+
bool first_field_odd; /* true if odd-indexed scanlines go to first field */
112117
s16 scale_v; /* V scale in 2.8 format (for power-of-2 CIC rates) */
113118
s16 scale_u; /* U scale in 2.8 format (for power-of-2 CIC rates) */
114119
u16 scale_y; /* Y scale in 2.8 format (for power-of-2 CIC rates) */
@@ -166,13 +171,13 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
166171
.scale_luma = 0x8c9a,
167172
.scale_sync = 0x3851,
168173
.scale_burst_chroma = 0x11195561,
169-
.misc = 0x00094c02, /* 5-tap FIR, SEQ_EN, 2 flds, 4 fld sync, ilace */
174+
.misc = 0x00094c00, /* 5-tap FIR, SEQ_EN, 2 flds, 4 fld sync */
170175
.nco_freq = 0x087c1f07c1f07c1f,
171176
.timing_regs = {
172177
0x03e10cc6, 0x0d6801fb, 0x023d034c, 0x00f80b6d,
173-
0x00000005, 0x0006000b, 0x000c0011, 0x000a0107,
174-
0x0111020d, 0x00000000, 0x00000000, 0x011c020d,
175-
0x00150106, 0x0107011b,
178+
0x0207020c, 0x00000005, 0x0006000b, 0x00070104,
179+
0x010e020a, 0x00000000, 0x00000000, 0x0119020a,
180+
0x00120103, 0x01040118,
176181
},
177182
},
178183
}, {
@@ -215,7 +220,7 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
215220
.scale_luma = 0x89d8,
216221
.scale_sync = 0x3c00,
217222
.scale_burst_chroma = 0x0caf53b5,
218-
.misc = 0x0009dc03, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, ilace, PAL */
223+
.misc = 0x0009dc01, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, PAL */
219224
.nco_freq = 0x0a8262b2cc48c1d1,
220225
.timing_regs = {
221226
0x04660cee, 0x0d8001fb, 0x025c034f, 0x00fd0b84,
@@ -241,7 +246,7 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
241246
.scale_luma = 0x89d8,
242247
.scale_sync = 0x3851,
243248
.scale_burst_chroma = 0x0d5c53b5,
244-
.misc = 0x00091c01, /* 5-tap FIR, SEQ_EN, 8 fld sync PAL */
249+
.misc = 0x00091c01, /* 5-tap FIR, SEQ_EN, 8 fld sync, PAL */
245250
.nco_freq = 0x0879bbf8d6d33ea8,
246251
.timing_regs = {
247252
0x03e10cc6, 0x0d6801fb, 0x023c034c, 0x00f80b6e,
@@ -264,11 +269,11 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
264269
.scale_luma = 0x89d8,
265270
.scale_sync = 0x3851,
266271
.scale_burst_chroma = 0x0d5c53b5,
267-
.misc = 0x0009dc03, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, ilace, PAL */
272+
.misc = 0x0009dc01, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, PAL */
268273
.nco_freq = 0x0879bbf8d6d33ea8,
269274
.timing_regs = {
270275
0x03e10cc6, 0x0d6801fb, 0x023c034c, 0x00f80b6e,
271-
0x00140019, 0x00000005, 0x0006000b, 0x00090103,
276+
0x0207020c, 0x00000005, 0x0006000b, 0x00090103,
272277
0x010f0209, 0x00080102, 0x010e020a, 0x0119020a,
273278
0x00120103, 0x01040118,
274279
},
@@ -293,13 +298,13 @@ static const struct rp1vec_hwmode rp1vec_vintage_modes[2] = {
293298
.scale_luma = 0x89d8,
294299
.scale_sync = 0x3c00,
295300
.scale_burst_chroma = 0,
296-
.misc = 0x00084002, /* 5-tap FIR, 2 fields, interlace */
301+
.misc = 0x00084000, /* 5-tap FIR, 2 fields */
297302
.nco_freq = 0,
298303
.timing_regs = {
299304
0x06f01430, 0x14d503cc, 0x00000000, 0x000010de,
300-
0x00000000, 0x00000007, 0x00000000, 0x00000000,
301-
0x00000000, 0x00000000, 0x00000000, 0x00d90195,
302-
0x000e00ca, 0x00cb00d8,
305+
0x03000300, 0x018d0194, 0x03000300, 0x00000000,
306+
0x00000000, 0x00000000, 0x00000000, 0x00d50191,
307+
0x000a00c6, 0x00c700d4,
303308
},
304309
}, {
305310
.max_rows_per_field = 369,
@@ -316,7 +321,7 @@ static const struct rp1vec_hwmode rp1vec_vintage_modes[2] = {
316321
.scale_luma = 0x89d8,
317322
.scale_sync = 0x3b13,
318323
.scale_burst_chroma = 0,
319-
.misc = 0x00084002, /* 5-tap FIR, 2 fields, interlace */
324+
.misc = 0x00084000, /* 5-tap FIR, 2 fields */
320325
.nco_freq = 0,
321326
.timing_regs = {
322327
0x03c10a08, 0x0a4d0114, 0x00000000, 0x000008a6,
@@ -429,7 +434,12 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
429434
vpad_b = ((mode->vsync_start - hwm->ref_vfp) >> (hwm->interlaced || vec->fake_31khz)) - h;
430435
vpad_b = min(max(0, vpad_b), hwm->max_rows_per_field - h);
431436

432-
/* Configure the hardware "front end" (in the sysclock domain) */
437+
/*
438+
* Configure the hardware "front end" (in the sysclock domain).
439+
* Note: To support 60fps update (per-field buffer flips), we no longer
440+
* enable VEC's native interlaced mode (which can't flip in mid-frame).
441+
* Instead, send individual fields, using software to flip between them.
442+
*/
433443
VEC_WRITE(VEC_APB_TIMEOUT, 0x38);
434444
VEC_WRITE(VEC_QOS,
435445
BITS(VEC_QOS_DQOS, 0x0) |
@@ -459,9 +469,7 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
459469
BITS(VEC_MODE_VFP_EN, (vpad_b > 0)) |
460470
BITS(VEC_MODE_VBP_EN, (hwm->max_rows_per_field > h + vpad_b)) |
461471
BITS(VEC_MODE_HFP_EN, (hpad_r > 0)) |
462-
BITS(VEC_MODE_HBP_EN, (wmax > w + hpad_r)) |
463-
BITS(VEC_MODE_FIELDS_PER_FRAME_MINUS1, hwm->interlaced) |
464-
BITS(VEC_MODE_FIRST_FIELD_ODD, hwm->first_field_odd));
472+
BITS(VEC_MODE_HBP_EN, (wmax > w + hpad_r)));
465473

466474
/* Configure the hardware "back end" (in the VDAC clock domain) */
467475
VEC_WRITE(VEC_DAC_80,
@@ -509,6 +517,11 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
509517
VEC_WRITE(VEC_DAC_EC, misc | rp1vec_rate_shift_table[rate - 4]);
510518
rp1vec_write_regs(vec, 0xDC, rp1vec_fir_regs, ARRAY_SIZE(rp1vec_fir_regs));
511519

520+
/* State for software-based field flipping */
521+
vec->field_flip = hwm->interlaced;
522+
vec->lower_field_flag = hwm->first_field_odd;
523+
vec->last_dma_addr = 0;
524+
512525
/* Set up interrupts and initialise VEC. It will start on the next rp1vec_hw_update() */
513526
VEC_WRITE(VEC_IRQ_FLAGS, 0xFFFFFFFFu);
514527
rp1vec_hw_vblank_ctrl(vec, 1);
@@ -525,32 +538,49 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
525538

526539
void rp1vec_hw_update(struct rp1_vec *vec, dma_addr_t addr, u32 offset, u32 stride)
527540
{
541+
unsigned long flags;
542+
543+
addr += offset;
544+
528545
/*
529546
* Update STRIDE, DMAH and DMAL only. When called after rp1vec_hw_setup(),
530547
* DMA starts immediately; if already running, the buffer will flip at
531-
* the next vertical sync event.
548+
* the next vertical sync event. For field-rate update in interlaced modes,
549+
* we need to adjust the address and stride to display the current field,
550+
* saving the original address (so it can be flipped for subsequent fields).
532551
*/
533-
u64 a = addr + offset;
552+
spin_lock_irqsave(&vec->hw_lock, flags);
534553

535-
if (vec->fake_31khz) {
536-
a += stride;
554+
vec->last_dma_addr = addr;
555+
vec->last_stride = stride;
556+
if (vec->field_flip || vec->fake_31khz) {
557+
if (vec->fake_31khz || vec->lower_field_flag)
558+
addr += stride;
537559
stride *= 2;
538560
}
539561
VEC_WRITE(VEC_DMA_STRIDE, stride);
540-
VEC_WRITE(VEC_DMA_ADDR_H, a >> 32);
541-
VEC_WRITE(VEC_DMA_ADDR_L, a & 0xFFFFFFFFu);
562+
VEC_WRITE(VEC_DMA_ADDR_H, addr >> 32);
563+
VEC_WRITE(VEC_DMA_ADDR_L, addr & 0xFFFFFFFFu);
564+
565+
spin_unlock_irqrestore(&vec->hw_lock, flags);
542566
}
543567

544568
void rp1vec_hw_stop(struct rp1_vec *vec)
545569
{
570+
unsigned long flags;
571+
546572
/*
547573
* Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
548574
* the current and any queued frame to end. "Force drain" flags are not used,
549575
* as they seem to prevent DMA from re-starting properly; it's safer to wait.
550576
*/
551577

578+
spin_lock_irqsave(&vec->hw_lock, flags);
579+
vec->last_dma_addr = 0;
552580
reinit_completion(&vec->finished);
553581
VEC_WRITE(VEC_CONTROL, 0);
582+
spin_unlock_irqrestore(&vec->hw_lock, flags);
583+
554584
if (!wait_for_completion_timeout(&vec->finished, HZ / 10))
555585
drm_err(&vec->drm, "%s: timed out waiting for idle\n", __func__);
556586
VEC_WRITE(VEC_IRQ_ENABLES, 0);
@@ -559,9 +589,10 @@ void rp1vec_hw_stop(struct rp1_vec *vec)
559589
void rp1vec_hw_vblank_ctrl(struct rp1_vec *vec, int enable)
560590
{
561591
VEC_WRITE(VEC_IRQ_ENABLES,
562-
BITS(VEC_IRQ_ENABLES_DONE, 1) |
563-
BITS(VEC_IRQ_ENABLES_DMA, (enable ? 1 : 0)) |
564-
BITS(VEC_IRQ_ENABLES_MATCH_ROW, 1023));
592+
BITS(VEC_IRQ_ENABLES_DONE, 1) |
593+
BITS(VEC_IRQ_ENABLES_DMA, (enable ? 1 : 0)) |
594+
BITS(VEC_IRQ_ENABLES_MATCH, vec->field_flip) |
595+
BITS(VEC_IRQ_ENABLES_MATCH_ROW, 32));
565596
}
566597

567598
irqreturn_t rp1vec_hw_isr(int irq, void *dev)
@@ -575,6 +606,29 @@ irqreturn_t rp1vec_hw_isr(int irq, void *dev)
575606
drm_crtc_handle_vblank(&vec->pipe.crtc);
576607
if (u & VEC_IRQ_FLAGS_DONE_BITS)
577608
complete(&vec->finished);
609+
610+
/*
611+
* VEC has native support for interlaced modes, but that only
612+
* supports buffer-flips per frame (30fps), not field (60fps).
613+
* Instead, we always run the VEC front end in a "progressive"
614+
* mode and use the "field-flip" trick (see RP1 DPI driver).
615+
*/
616+
if ((u & VEC_IRQ_FLAGS_MATCH_BITS) && vec->field_flip) {
617+
unsigned long flags;
618+
dma_addr_t a;
619+
620+
spin_lock_irqsave(&vec->hw_lock, flags);
621+
vec->lower_field_flag = !vec->lower_field_flag;
622+
a = vec->last_dma_addr;
623+
if (a) {
624+
if (vec->lower_field_flag)
625+
a += vec->last_stride;
626+
VEC_WRITE(VEC_DMA_ADDR_H, a >> 32);
627+
VEC_WRITE(VEC_DMA_ADDR_L, a & 0xFFFFFFFFu);
628+
}
629+
spin_unlock_irqrestore(&vec->hw_lock, flags);
630+
}
578631
}
632+
579633
return u ? IRQ_HANDLED : IRQ_NONE;
580634
}

0 commit comments

Comments
 (0)