Skip to content

Commit 5cb6bc1

Browse files
drm: rp1: rp1-vec: First attempt at using "field wobble "for interlace
This works around the 30fps limitation when using VEC's "native" interlaced modes in the front end. The technique is adapted from the RP1 DPI driver (DPI does not have a native interlaced mode). Some register changes were required in the VEC back end to make this work, in particular for NTSC timings. Signed-off-by: Nick Hollinghurst <[email protected]>
1 parent 6a97c88 commit 5cb6bc1

File tree

3 files changed

+96
-10
lines changed

3 files changed

+96
-10
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ static int rp1vec_platform_probe(struct platform_device *pdev)
470470
return ret;
471471
}
472472
vec->pdev = pdev;
473+
spin_lock_init(&vec->hw_lock);
473474

474475
for (i = 0; i < RP1VEC_NUM_HW_BLOCKS; i++) {
475476
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 interlaced;
58+
bool lower_field_flag;
5359
};
5460

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

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

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#define VEC_WRITE(reg, val) writel((val), vec->hw_base[RP1VEC_HW_BLOCK_VEC] + (reg ## _OFFSET))
2323
#define VEC_READ(reg) readl(vec->hw_base[RP1VEC_HW_BLOCK_VEC] + (reg ## _OFFSET))
2424

25+
/* Experimental mode for interlace with 60fps buffer flips */
26+
#define FIELD_WOBBLE
27+
2528
static void rp1vec_write_regs(struct rp1_vec *vec, u32 offset, u32 const *vals, u32 num)
2629
{
2730
while (num--) {
@@ -168,12 +171,29 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
168171
.scale_burst_chroma = 0x11195561,
169172
.misc = 0x00094c02, /* 5-tap FIR, SEQ_EN, 2 flds, 4 fld sync, ilace */
170173
.nco_freq = 0x087c1f07c1f07c1f,
174+
#ifdef FIELD_WOBBLE
175+
/*
176+
* NOTE: For the "field-wobble" interlace technique
177+
* (used to achieve 60fps buffer-flip rate) to work,
178+
* we had to offset the vertical timings so that VSYNC
179+
* starts at half-line 0; all image line numbers are
180+
* reduced by 3 compared to NTSC standard numbering.
181+
* This should have no effect on the actual frame.
182+
*/
183+
.timing_regs = {
184+
0x03e10cc6, 0x0d6801fb, 0x023d034c, 0x00f80b6d,
185+
0x0207020c, 0x00000005, 0x0006000b, 0x00070104,
186+
0x010e020a, 0x00000000, 0x00000000, 0x0119020a,
187+
0x00120103, 0x01040118,
188+
},
189+
#else
171190
.timing_regs = {
172191
0x03e10cc6, 0x0d6801fb, 0x023d034c, 0x00f80b6d,
173192
0x00000005, 0x0006000b, 0x000c0011, 0x000a0107,
174193
0x0111020d, 0x00000000, 0x00000000, 0x011c020d,
175194
0x00150106, 0x0107011b,
176195
},
196+
#endif
177197
},
178198
}, {
179199
/* PAL */
@@ -268,7 +288,7 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
268288
.nco_freq = 0x0879bbf8d6d33ea8,
269289
.timing_regs = {
270290
0x03e10cc6, 0x0d6801fb, 0x023c034c, 0x00f80b6e,
271-
0x00140019, 0x00000005, 0x0006000b, 0x00090103,
291+
0x00140019, 0x00000005, 0x0006000b, 0x00090103, // XXX [0x94] looks dodgy to me?
272292
0x010f0209, 0x00080102, 0x010e020a, 0x0119020a,
273293
0x00120103, 0x01040118,
274294
},
@@ -460,8 +480,11 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
460480
BITS(VEC_MODE_VBP_EN, (hwm->max_rows_per_field > h + vpad_b)) |
461481
BITS(VEC_MODE_HFP_EN, (hpad_r > 0)) |
462482
BITS(VEC_MODE_HBP_EN, (wmax > w + hpad_r)) |
483+
#ifndef FIELD_WOBBLE /* Clear these for field-wobble! */
463484
BITS(VEC_MODE_FIELDS_PER_FRAME_MINUS1, hwm->interlaced) |
464-
BITS(VEC_MODE_FIRST_FIELD_ODD, hwm->first_field_odd));
485+
BITS(VEC_MODE_FIRST_FIELD_ODD, hwm->first_field_odd) |
486+
#endif
487+
0);
465488

466489
/* Configure the hardware "back end" (in the VDAC clock domain) */
467490
VEC_WRITE(VEC_DAC_80,
@@ -492,6 +515,9 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
492515
VEC_WRITE(VEC_DAC_CC, (tvstd >= DRM_MODE_TV_MODE_SECAM) ? 0 : hwm->scale_burst_chroma);
493516
VEC_WRITE(VEC_DAC_D0, 0x02000000); /* ADC offsets -- not needed in RP1? */
494517
misc = hwm->misc;
518+
#ifdef FIELD_WOBBLE
519+
misc &= ~2; /* For field-wobble: Clear the "fields_per_frame_minus1" flag! */
520+
#endif
495521
if ((tvstd == DRM_MODE_TV_MODE_NTSC_443 || tvstd == DRM_MODE_TV_MODE_PAL) &&
496522
mode_family != 1) {
497523
/* Change colour carrier frequency to 4433618.75 Hz; disable hard sync */
@@ -509,6 +535,15 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
509535
VEC_WRITE(VEC_DAC_EC, misc | rp1vec_rate_shift_table[rate - 4]);
510536
rp1vec_write_regs(vec, 0xDC, rp1vec_fir_regs, ARRAY_SIZE(rp1vec_fir_regs));
511537

538+
#ifdef FIELD_WOBBLE
539+
vec->interlaced = hwm->interlaced;
540+
vec->lower_field_flag = hwm->first_field_odd;
541+
#else
542+
vec->interlaced = false;
543+
vec->lower_field_flag = false;
544+
#endif
545+
vec->last_dma_addr = 0;
546+
512547
/* Set up interrupts and initialise VEC. It will start on the next rp1vec_hw_update() */
513548
VEC_WRITE(VEC_IRQ_FLAGS, 0xFFFFFFFFu);
514549
rp1vec_hw_vblank_ctrl(vec, 1);
@@ -525,32 +560,48 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
525560

526561
void rp1vec_hw_update(struct rp1_vec *vec, dma_addr_t addr, u32 offset, u32 stride)
527562
{
563+
unsigned long flags;
564+
565+
spin_lock_irqsave(&vec->hw_lock, flags);
566+
528567
/*
529568
* Update STRIDE, DMAH and DMAL only. When called after rp1vec_hw_setup(),
530569
* DMA starts immediately; if already running, the buffer will flip at
531-
* the next vertical sync event.
570+
* the next vertical sync event. In interlaced mode, we need to adjust
571+
* the address and stride to display only the current field, saving
572+
* the original address (so it can be flipped for subsequent fields).
532573
*/
533-
u64 a = addr + offset;
534-
535-
if (vec->fake_31khz) {
536-
a += stride;
574+
addr += offset;
575+
vec->last_dma_addr = addr;
576+
vec->last_stride = stride;
577+
if (vec->fake_31khz || vec->interlaced) {
578+
if (vec->fake_31khz || vec->lower_field_flag)
579+
addr += stride;
537580
stride *= 2;
538581
}
539582
VEC_WRITE(VEC_DMA_STRIDE, stride);
540-
VEC_WRITE(VEC_DMA_ADDR_H, a >> 32);
541-
VEC_WRITE(VEC_DMA_ADDR_L, a & 0xFFFFFFFFu);
583+
VEC_WRITE(VEC_DMA_ADDR_H, addr >> 32);
584+
VEC_WRITE(VEC_DMA_ADDR_L, addr & 0xFFFFFFFFu);
585+
586+
spin_unlock_irqrestore(&vec->hw_lock, flags);
542587
}
543588

544589
void rp1vec_hw_stop(struct rp1_vec *vec)
545590
{
591+
unsigned long flags;
592+
546593
/*
547594
* Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
548595
* the current and any queued frame to end. "Force drain" flags are not used,
549596
* as they seem to prevent DMA from re-starting properly; it's safer to wait.
550597
*/
551598

599+
spin_lock_irqsave(&vec->hw_lock, flags);
600+
vec->last_dma_addr = 0;
552601
reinit_completion(&vec->finished);
553602
VEC_WRITE(VEC_CONTROL, 0);
603+
spin_unlock_irqrestore(&vec->hw_lock, flags);
604+
554605
if (!wait_for_completion_timeout(&vec->finished, HZ / 10))
555606
drm_err(&vec->drm, "%s: timed out waiting for idle\n", __func__);
556607
VEC_WRITE(VEC_IRQ_ENABLES, 0);
@@ -561,7 +612,10 @@ void rp1vec_hw_vblank_ctrl(struct rp1_vec *vec, int enable)
561612
VEC_WRITE(VEC_IRQ_ENABLES,
562613
BITS(VEC_IRQ_ENABLES_DONE, 1) |
563614
BITS(VEC_IRQ_ENABLES_DMA, (enable ? 1 : 0)) |
564-
BITS(VEC_IRQ_ENABLES_MATCH_ROW, 1023));
615+
#ifdef FIELD_WOBBLE
616+
BITS(VEC_IRQ_ENABLES_MATCH, vec->interlaced) |
617+
#endif
618+
BITS(VEC_IRQ_ENABLES_MATCH_ROW, 32));
565619
}
566620

567621
irqreturn_t rp1vec_hw_isr(int irq, void *dev)
@@ -575,6 +629,31 @@ irqreturn_t rp1vec_hw_isr(int irq, void *dev)
575629
drm_crtc_handle_vblank(&vec->pipe.crtc);
576630
if (u & VEC_IRQ_FLAGS_DONE_BITS)
577631
complete(&vec->finished);
632+
633+
#ifdef FIELD_WOBBLE
634+
/*
635+
* VEC has native support for interlaced modes, but that only
636+
* supports buffer-flips per frame (30fps), not field (60fps).
637+
* Instead, we always run the VEC front end in a "progressive"
638+
* mode and use the "field-wobble" trick (see RP1 DPI driver).
639+
*/
640+
if ((u & VEC_IRQ_FLAGS_MATCH_BITS) && vec->interlaced) {
641+
unsigned long flags;
642+
dma_addr_t a;
643+
644+
spin_lock_irqsave(&vec->hw_lock, flags);
645+
vec->lower_field_flag = !vec->lower_field_flag;
646+
a = vec->last_dma_addr;
647+
if (a) {
648+
if (vec->lower_field_flag)
649+
a += vec->last_stride;
650+
VEC_WRITE(VEC_DMA_ADDR_H, a >> 32);
651+
VEC_WRITE(VEC_DMA_ADDR_L, a & 0xFFFFFFFFu);
652+
}
653+
spin_unlock_irqrestore(&vec->hw_lock, flags);
654+
}
655+
#endif
578656
}
657+
579658
return u ? IRQ_HANDLED : IRQ_NONE;
580659
}

0 commit comments

Comments
 (0)