Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,9 @@ static int rp1vec_connector_get_modes(struct drm_connector *connector)
mode->vdisplay >>= 1;
mode->vsync_start >>= 1;
mode->vsync_end >>= 1;
mode->vtotal >>= 1;
mode->vtotal >>= 1;
if (mode->vtotal == 262 && tvstd < DRM_MODE_TV_MODE_PAL)
mode->vtotal++;
} else if (mode->hdisplay == 704 && mode->vtotal == preferred_lines) {
mode->type |= DRM_MODE_TYPE_PREFERRED;
}
Expand Down Expand Up @@ -468,6 +470,7 @@ static int rp1vec_platform_probe(struct platform_device *pdev)
return ret;
}
vec->pdev = pdev;
spin_lock_init(&vec->hw_lock);

for (i = 0; i < RP1VEC_NUM_HW_BLOCKS; i++) {
vec->hw_base[i] =
Expand Down
6 changes: 6 additions & 0 deletions drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ struct rp1_vec {
u32 cur_fmt;
bool fake_31khz, vec_running, pipe_enabled;
struct completion finished;

spinlock_t hw_lock; /* the following are used in line-match ISR */
dma_addr_t last_dma_addr;
u32 last_stride;
bool field_flip;
bool lower_field_flag;
};

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

struct rp1vec_hwmode {
u16 max_rows_per_field; /* active lines per field (including partial ones) */
u16 ref_vfp; /* nominal (vsync_start - vdisplay) when max height */
bool interlaced; /* set for interlaced */
bool first_field_odd; /* depends confusingly on line numbering convention */
bool first_field_odd; /* true if odd-indexed scanlines go to first field */
s16 scale_v; /* V scale in 2.8 format (for power-of-2 CIC rates) */
s16 scale_u; /* U scale in 2.8 format (for power-of-2 CIC rates) */
u16 scale_y; /* Y scale in 2.8 format (for power-of-2 CIC rates) */
Expand Down Expand Up @@ -166,13 +171,13 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
.scale_luma = 0x8c9a,
.scale_sync = 0x3851,
.scale_burst_chroma = 0x11195561,
.misc = 0x00094c02, /* 5-tap FIR, SEQ_EN, 2 flds, 4 fld sync, ilace */
.misc = 0x00094c00, /* 5-tap FIR, SEQ_EN, 2 flds, 4 fld sync */
.nco_freq = 0x087c1f07c1f07c1f,
.timing_regs = {
0x03e10cc6, 0x0d6801fb, 0x023d034c, 0x00f80b6d,
0x00000005, 0x0006000b, 0x000c0011, 0x000a0107,
0x0111020d, 0x00000000, 0x00000000, 0x011c020d,
0x00150106, 0x0107011b,
0x0207020c, 0x00000005, 0x0006000b, 0x00070104,
0x010e020a, 0x00000000, 0x00000000, 0x0119020a,
0x00120103, 0x01040118,
},
},
}, {
Expand Down Expand Up @@ -215,7 +220,7 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
.scale_luma = 0x89d8,
.scale_sync = 0x3c00,
.scale_burst_chroma = 0x0caf53b5,
.misc = 0x0009dc03, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, ilace, PAL */
.misc = 0x0009dc01, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, PAL */
.nco_freq = 0x0a8262b2cc48c1d1,
.timing_regs = {
0x04660cee, 0x0d8001fb, 0x025c034f, 0x00fd0b84,
Expand All @@ -241,7 +246,7 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
.scale_luma = 0x89d8,
.scale_sync = 0x3851,
.scale_burst_chroma = 0x0d5c53b5,
.misc = 0x00091c01, /* 5-tap FIR, SEQ_EN, 8 fld sync PAL */
.misc = 0x00091c01, /* 5-tap FIR, SEQ_EN, 8 fld sync, PAL */
.nco_freq = 0x0879bbf8d6d33ea8,
.timing_regs = {
0x03e10cc6, 0x0d6801fb, 0x023c034c, 0x00f80b6e,
Expand All @@ -264,11 +269,11 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
.scale_luma = 0x89d8,
.scale_sync = 0x3851,
.scale_burst_chroma = 0x0d5c53b5,
.misc = 0x0009dc03, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, ilace, PAL */
.misc = 0x0009dc01, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, PAL */
.nco_freq = 0x0879bbf8d6d33ea8,
.timing_regs = {
0x03e10cc6, 0x0d6801fb, 0x023c034c, 0x00f80b6e,
0x00140019, 0x00000005, 0x0006000b, 0x00090103,
0x0207020c, 0x00000005, 0x0006000b, 0x00090103,
0x010f0209, 0x00080102, 0x010e020a, 0x0119020a,
0x00120103, 0x01040118,
},
Expand All @@ -293,13 +298,13 @@ static const struct rp1vec_hwmode rp1vec_vintage_modes[2] = {
.scale_luma = 0x89d8,
.scale_sync = 0x3c00,
.scale_burst_chroma = 0,
.misc = 0x00084002, /* 5-tap FIR, 2 fields, interlace */
.misc = 0x00084000, /* 5-tap FIR, 2 fields */
.nco_freq = 0,
.timing_regs = {
0x06f01430, 0x14d503cc, 0x00000000, 0x000010de,
0x00000000, 0x00000007, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00d90195,
0x000e00ca, 0x00cb00d8,
0x03000300, 0x018d0194, 0x03000300, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00d50191,
0x000a00c6, 0x00c700d4,
},
}, {
.max_rows_per_field = 369,
Expand All @@ -316,7 +321,7 @@ static const struct rp1vec_hwmode rp1vec_vintage_modes[2] = {
.scale_luma = 0x89d8,
.scale_sync = 0x3b13,
.scale_burst_chroma = 0,
.misc = 0x00084002, /* 5-tap FIR, 2 fields, interlace */
.misc = 0x00084000, /* 5-tap FIR, 2 fields */
.nco_freq = 0,
.timing_regs = {
0x03c10a08, 0x0a4d0114, 0x00000000, 0x000008a6,
Expand Down Expand Up @@ -429,7 +434,12 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
vpad_b = ((mode->vsync_start - hwm->ref_vfp) >> (hwm->interlaced || vec->fake_31khz)) - h;
vpad_b = min(max(0, vpad_b), hwm->max_rows_per_field - h);

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

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

/* State for software-based field flipping */
vec->field_flip = hwm->interlaced;
vec->lower_field_flag = hwm->first_field_odd;
vec->last_dma_addr = 0;

/* Set up interrupts and initialise VEC. It will start on the next rp1vec_hw_update() */
VEC_WRITE(VEC_IRQ_FLAGS, 0xFFFFFFFFu);
rp1vec_hw_vblank_ctrl(vec, 1);
Expand All @@ -525,32 +538,49 @@ void rp1vec_hw_setup(struct rp1_vec *vec,

void rp1vec_hw_update(struct rp1_vec *vec, dma_addr_t addr, u32 offset, u32 stride)
{
unsigned long flags;

addr += offset;

/*
* Update STRIDE, DMAH and DMAL only. When called after rp1vec_hw_setup(),
* DMA starts immediately; if already running, the buffer will flip at
* the next vertical sync event.
* the next vertical sync event. For field-rate update in interlaced modes,
* we need to adjust the address and stride to display the current field,
* saving the original address (so it can be flipped for subsequent fields).
*/
u64 a = addr + offset;
spin_lock_irqsave(&vec->hw_lock, flags);

if (vec->fake_31khz) {
a += stride;
vec->last_dma_addr = addr;
vec->last_stride = stride;
if (vec->field_flip || vec->fake_31khz) {
if (vec->fake_31khz || vec->lower_field_flag)
addr += stride;
stride *= 2;
}
VEC_WRITE(VEC_DMA_STRIDE, stride);
VEC_WRITE(VEC_DMA_ADDR_H, a >> 32);
VEC_WRITE(VEC_DMA_ADDR_L, a & 0xFFFFFFFFu);
VEC_WRITE(VEC_DMA_ADDR_H, addr >> 32);
VEC_WRITE(VEC_DMA_ADDR_L, addr & 0xFFFFFFFFu);

spin_unlock_irqrestore(&vec->hw_lock, flags);
}

void rp1vec_hw_stop(struct rp1_vec *vec)
{
unsigned long flags;

/*
* Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
* the current and any queued frame to end. "Force drain" flags are not used,
* as they seem to prevent DMA from re-starting properly; it's safer to wait.
*/

spin_lock_irqsave(&vec->hw_lock, flags);
vec->last_dma_addr = 0;
reinit_completion(&vec->finished);
VEC_WRITE(VEC_CONTROL, 0);
spin_unlock_irqrestore(&vec->hw_lock, flags);

if (!wait_for_completion_timeout(&vec->finished, HZ / 10))
drm_err(&vec->drm, "%s: timed out waiting for idle\n", __func__);
VEC_WRITE(VEC_IRQ_ENABLES, 0);
Expand All @@ -559,9 +589,10 @@ void rp1vec_hw_stop(struct rp1_vec *vec)
void rp1vec_hw_vblank_ctrl(struct rp1_vec *vec, int enable)
{
VEC_WRITE(VEC_IRQ_ENABLES,
BITS(VEC_IRQ_ENABLES_DONE, 1) |
BITS(VEC_IRQ_ENABLES_DMA, (enable ? 1 : 0)) |
BITS(VEC_IRQ_ENABLES_MATCH_ROW, 1023));
BITS(VEC_IRQ_ENABLES_DONE, 1) |
BITS(VEC_IRQ_ENABLES_DMA, (enable ? 1 : 0)) |
BITS(VEC_IRQ_ENABLES_MATCH, vec->field_flip) |
BITS(VEC_IRQ_ENABLES_MATCH_ROW, 32));
}

irqreturn_t rp1vec_hw_isr(int irq, void *dev)
Expand All @@ -575,6 +606,29 @@ irqreturn_t rp1vec_hw_isr(int irq, void *dev)
drm_crtc_handle_vblank(&vec->pipe.crtc);
if (u & VEC_IRQ_FLAGS_DONE_BITS)
complete(&vec->finished);

/*
* VEC has native support for interlaced modes, but that only
* supports buffer-flips per frame (30fps), not field (60fps).
* Instead, we always run the VEC front end in a "progressive"
* mode and use the "field-flip" trick (see RP1 DPI driver).
*/
if ((u & VEC_IRQ_FLAGS_MATCH_BITS) && vec->field_flip) {
unsigned long flags;
dma_addr_t a;

spin_lock_irqsave(&vec->hw_lock, flags);
vec->lower_field_flag = !vec->lower_field_flag;
a = vec->last_dma_addr;
if (a) {
if (vec->lower_field_flag)
a += vec->last_stride;
VEC_WRITE(VEC_DMA_ADDR_H, a >> 32);
VEC_WRITE(VEC_DMA_ADDR_L, a & 0xFFFFFFFFu);
}
spin_unlock_irqrestore(&vec->hw_lock, flags);
}
}

return u ? IRQ_HANDLED : IRQ_NONE;
}