Skip to content

Commit 04b88e1

Browse files
drm: rp1: rp1-vec: Allow non-standard modes with various crops
Tweak sync timings in the advertised modelines. Accept other, custom modes, provided they fit within the active area of one of the existing hardware-supported TV modes. Instead of always padding symmetrically, try to respect the user's [hv]sync_start values, allowing the image to be shifted around the screen (to fine-tune overscan correction). Signed-off-by: Nick Hollinghurst <[email protected]>
1 parent 833881c commit 04b88e1

File tree

2 files changed

+96
-44
lines changed

2 files changed

+96
-44
lines changed

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

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include <linux/list.h>
2020
#include <linux/platform_device.h>
2121
#include <linux/clk.h>
22-
#include <linux/printk.h>
2322
#include <linux/console.h>
2423
#include <linux/debugfs.h>
2524
#include <linux/uaccess.h>
@@ -208,26 +207,26 @@ static void rp1vec_connector_destroy(struct drm_connector *connector)
208207
static const struct drm_display_mode rp1vec_modes[4] = {
209208
{ /* Full size 525/60i with Rec.601 pixel rate */
210209
DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
211-
720, 720 + 14, 720 + 14 + 64, 858, 0,
212-
480, 480 + 7, 480 + 7 + 6, 525, 0,
210+
720, 720 + 16, 720 + 16 + 64, 858, 0,
211+
480, 480 + 6, 480 + 6 + 6, 525, 0,
213212
DRM_MODE_FLAG_INTERLACE)
214213
},
215214
{ /* Cropped and horizontally squashed to be TV-safe */
216215
DRM_MODE("704x432i", DRM_MODE_TYPE_DRIVER, 15429,
217-
704, 704 + 72, 704 + 72 + 72, 980, 0,
218-
432, 432 + 31, 432 + 31 + 6, 525, 0,
216+
704, 704 + 76, 704 + 76 + 72, 980, 0,
217+
432, 432 + 30, 432 + 30 + 6, 525, 0,
219218
DRM_MODE_FLAG_INTERLACE)
220219
},
221220
{ /* Full size 625/50i with Rec.601 pixel rate */
222221
DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
223222
720, 720 + 20, 720 + 20 + 64, 864, 0,
224-
576, 576 + 4, 576 + 4 + 6, 625, 0,
223+
576, 576 + 5, 576 + 5 + 5, 625, 0,
225224
DRM_MODE_FLAG_INTERLACE)
226225
},
227226
{ /* Cropped and squashed, for square(ish) pixels */
228227
DRM_MODE("704x512i", DRM_MODE_TYPE_DRIVER, 15429,
229228
704, 704 + 80, 704 + 80 + 72, 987, 0,
230-
512, 512 + 36, 512 + 36 + 6, 625, 0,
229+
512, 512 + 37, 512 + 37 + 5, 625, 0,
231230
DRM_MODE_FLAG_INTERLACE)
232231
}
233232
};
@@ -298,27 +297,42 @@ static enum drm_mode_status rp1vec_mode_valid(struct drm_device *dev,
298297
const struct drm_display_mode *mode)
299298
{
300299
/*
301-
* Check the mode roughly matches one of our standard modes
302-
* (optionally half-height and progressive). Ignore H/V sync
303-
* timings which for interlaced TV are approximate at best.
300+
* Check the mode roughly matches something we can generate.
301+
* The hardware driver is very prescriptive about pixel clocks,
302+
* line and frame durations, but we'll tolerate rounding errors.
303+
* Within each hardware mode, allow image size and position to vary
304+
* (to fine-tune overscan correction or emulate retro devices).
305+
* Don't check sync timings here: the HW driver will sanitize them.
304306
*/
305-
int i, prog;
306307

307-
prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
308+
int prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
309+
int vtotal_full = mode->vtotal << prog;
310+
int vdisplay_full = mode->vdisplay << prog;
311+
312+
/* Reject very small frames */
313+
if (vtotal_full < 256 || mode->hdisplay < 256)
314+
return MODE_BAD;
315+
316+
/* Check lines, frame period (ms) and vertical size limit */
317+
if (vtotal_full >= 524 && vtotal_full <= 526 &&
318+
mode->htotal * vtotal_full > 33 * mode->clock &&
319+
mode->htotal * vtotal_full < 34 * mode->clock &&
320+
vdisplay_full <= 480)
321+
goto vgood;
322+
if (vtotal_full >= 624 && vtotal_full <= 626 &&
323+
mode->htotal * vtotal_full > 39 * mode->clock &&
324+
mode->htotal * vtotal_full < 41 * mode->clock &&
325+
vdisplay_full <= 576)
326+
goto vgood;
327+
return MODE_BAD;
308328

309-
for (i = 0; i < ARRAY_SIZE(rp1vec_modes); i++) {
310-
const struct drm_display_mode *ref = rp1vec_modes + i;
311-
312-
if (mode->hdisplay == ref->hdisplay &&
313-
mode->vdisplay == (ref->vdisplay >> prog) &&
314-
mode->clock + 2 >= ref->clock &&
315-
mode->clock <= ref->clock + 2 &&
316-
mode->htotal + 2 >= ref->htotal &&
317-
mode->htotal <= ref->htotal + 2 &&
318-
mode->vtotal + 2 >= (ref->vtotal >> prog) &&
319-
mode->vtotal <= (ref->vtotal >> prog) + 2)
320-
return MODE_OK;
321-
}
329+
vgood:
330+
/* Check pixel rate (kHz) and horizontal size limit */
331+
if (mode->clock == 13500 && mode->hdisplay <= 720)
332+
return MODE_OK;
333+
if (mode->clock >= 15428 && mode->clock <= 15429 &&
334+
mode->hdisplay <= 800)
335+
return MODE_OK;
322336
return MODE_BAD;
323337
}
324338

@@ -440,7 +454,7 @@ static int rp1vec_platform_probe(struct platform_device *pdev)
440454
ret = drmm_mode_config_init(drm);
441455
if (ret)
442456
goto err_free_drm;
443-
drm->mode_config.max_width = 768;
457+
drm->mode_config.max_width = 800;
444458
drm->mode_config.max_height = 576;
445459
drm->mode_config.fb_base = 0;
446460
drm->mode_config.preferred_depth = 32;

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

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include <linux/delay.h>
1212
#include <linux/interrupt.h>
1313
#include <linux/platform_device.h>
14-
#include <linux/printk.h>
1514
#include <drm/drm_fourcc.h>
1615
#include <drm/drm_print.h>
1716
#include <drm/drm_vblank.h>
@@ -82,13 +81,13 @@ static const struct rp1vec_ipixfmt my_formats[] = {
8281
/*
8382
* Hardware mode descriptions (@ 108 MHz clock rate).
8483
* These rely largely on "canned" register settings.
85-
* TODO: Port the generating software from FP to integer,
86-
* or better factorize the differences between modes.
8784
*/
8885

8986
struct rp1vec_hwmode {
90-
u16 total_cols; /* active columns, plus padding for filter context */
87+
u16 total_cols; /* max active columns incl. padding and windowing */
9188
u16 rows_per_field; /* active lines per field (including partial ones) */
89+
u16 ref_hfp; /* nominal (hsync_start - hdisplay) when max width */
90+
u16 ref_vfp; /* nominal (vsync_start - vdisplay) when max height */
9291
bool interlaced; /* set for interlaced */
9392
bool first_field_odd; /* set for interlaced and 30fps */
9493
u32 yuv_scaling; /* three 10-bit fields {Y, U, V} in 2.8 format */
@@ -103,6 +102,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
103102
{
104103
.total_cols = 724,
105104
.rows_per_field = 240,
105+
.ref_hfp = 12,
106+
.ref_vfp = 2,
106107
.interlaced = false,
107108
.first_field_odd = false,
108109
.yuv_scaling = 0x1071d0cf,
@@ -118,6 +119,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
118119
}, {
119120
.total_cols = 815,
120121
.rows_per_field = 240,
122+
.ref_hfp = 16,
123+
.ref_vfp = 2,
121124
.interlaced = false,
122125
.first_field_odd = false,
123126
.yuv_scaling = 0x1c131962,
@@ -135,6 +138,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
135138
{
136139
.total_cols = 724,
137140
.rows_per_field = 243,
141+
.ref_hfp = 12,
142+
.ref_vfp = 3,
138143
.interlaced = true,
139144
.first_field_odd = true,
140145
.yuv_scaling = 0x1071d0cf,
@@ -150,6 +155,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
150155
}, {
151156
.total_cols = 815,
152157
.rows_per_field = 243,
158+
.ref_hfp = 16,
159+
.ref_vfp = 3,
153160
.interlaced = true,
154161
.first_field_odd = true,
155162
.yuv_scaling = 0x1c131962,
@@ -170,6 +177,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
170177
{
171178
.total_cols = 724,
172179
.rows_per_field = 288,
180+
.ref_hfp = 16,
181+
.ref_vfp = 2,
173182
.interlaced = false,
174183
.first_field_odd = false,
175184
.yuv_scaling = 0x11c1f8e0,
@@ -185,6 +194,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
185194
}, {
186195
.total_cols = 804,
187196
.rows_per_field = 288,
197+
.ref_hfp = 24,
198+
.ref_vfp = 2,
188199
.interlaced = false,
189200
.first_field_odd = false,
190201
.yuv_scaling = 0x1e635d7f,
@@ -202,6 +213,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
202213
{
203214
.total_cols = 724,
204215
.rows_per_field = 288,
216+
.ref_hfp = 16,
217+
.ref_vfp = 5,
205218
.interlaced = true,
206219
.first_field_odd = false,
207220
.yuv_scaling = 0x11c1f8e0,
@@ -217,6 +230,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
217230
}, {
218231
.total_cols = 804,
219232
.rows_per_field = 288,
233+
.ref_hfp = 24,
234+
.ref_vfp = 5,
220235
.interlaced = true,
221236
.first_field_odd = false,
222237
.yuv_scaling = 0x1e635d7f,
@@ -237,6 +252,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
237252
{
238253
.total_cols = 724,
239254
.rows_per_field = 240,
255+
.ref_hfp = 12,
256+
.ref_vfp = 2,
240257
.interlaced = false,
241258
.first_field_odd = false,
242259
.yuv_scaling = 0x11c1f8e0,
@@ -252,6 +269,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
252269
}, {
253270
.total_cols = 815,
254271
.rows_per_field = 240,
272+
.ref_hfp = 16,
273+
.ref_vfp = 2,
255274
.interlaced = false,
256275
.first_field_odd = false,
257276
.yuv_scaling = 0x1e635d7f,
@@ -269,6 +288,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
269288
{
270289
.total_cols = 724,
271290
.rows_per_field = 243,
291+
.ref_hfp = 12,
292+
.ref_vfp = 3,
272293
.interlaced = true,
273294
.first_field_odd = true,
274295
.yuv_scaling = 0x11c1f8e0,
@@ -284,6 +305,8 @@ static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
284305
}, {
285306
.total_cols = 815,
286307
.rows_per_field = 243,
308+
.ref_hfp = 16,
309+
.ref_vfp = 3,
287310
.interlaced = true,
288311
.first_field_odd = true,
289312
.yuv_scaling = 0x1e635d7f,
@@ -308,11 +331,11 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
308331
{
309332
unsigned int i, mode_family, mode_ilaced, mode_narrow;
310333
const struct rp1vec_hwmode *hwm;
311-
unsigned int w, h;
334+
int w, h, hpad, vpad;
312335

313336
/* Pick the appropriate "base" mode, which we may modify */
314337
mode_ilaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
315-
if (mode->vtotal > 263 * (1 + mode_ilaced))
338+
if (mode->vtotal >= 272 * (1 + mode_ilaced))
316339
mode_family = 1;
317340
else
318341
mode_family = (tvstd == RP1VEC_TVSTD_PAL_M || tvstd == RP1VEC_TVSTD_PAL60) ? 2 : 0;
@@ -326,13 +349,28 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
326349
tvstd, rp1vec_tvstd_names[tvstd]);
327350

328351
w = mode->hdisplay;
329-
h = mode->vdisplay;
330-
if (mode_ilaced)
331-
h >>= 1;
352+
h = mode->vdisplay >> mode_ilaced;
332353
if (w > hwm->total_cols)
333354
w = hwm->total_cols;
334355
if (h > hwm->rows_per_field)
335-
w = hwm->rows_per_field;
356+
h = hwm->rows_per_field;
357+
358+
/*
359+
* Add padding so a framebuffer with the given dimensions and
360+
* [hv]sync_start can be displayed in the chosen hardware mode.
361+
*
362+
* |<----- mode->hsync_start ----->|
363+
* |<------ w ------>| |
364+
* | | >|--|< ref_hfp
365+
* |<- hpad ->|
366+
* |<------------ total_cols ----------->|
367+
* ________FRAMEBUFFERCONTENTS__________
368+
* ' `--\____/-<\/\/\>-'
369+
*/
370+
hpad = max(0, mode->hsync_start - hwm->ref_hfp - w);
371+
hpad = min(hpad, hwm->total_cols - w);
372+
vpad = max(0, ((mode->vsync_start - hwm->ref_vfp) >> mode_ilaced) - h);
373+
vpad = min(vpad, hwm->rows_per_field - h);
336374

337375
/* Configure the hardware */
338376
VEC_WRITE(VEC_APB_TIMEOUT, 0x38);
@@ -347,18 +385,18 @@ void rp1vec_hw_setup(struct rp1_vec *vec,
347385
BITS(VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1, h - 1));
348386
VEC_WRITE(VEC_YUV_SCALING, hwm->yuv_scaling);
349387
VEC_WRITE(VEC_BACK_PORCH,
350-
BITS(VEC_BACK_PORCH_HBP_MINUS1, (hwm->total_cols - w - 1) >> 1) |
351-
BITS(VEC_BACK_PORCH_VBP_MINUS1, (hwm->rows_per_field - h - 1) >> 1));
388+
BITS(VEC_BACK_PORCH_HBP_MINUS1, hwm->total_cols - w - hpad - 1) |
389+
BITS(VEC_BACK_PORCH_VBP_MINUS1, hwm->rows_per_field - h - vpad - 1));
352390
VEC_WRITE(VEC_FRONT_PORCH,
353-
BITS(VEC_FRONT_PORCH_HFP_MINUS1, (hwm->total_cols - w - 2) >> 1) |
354-
BITS(VEC_FRONT_PORCH_VFP_MINUS1, (hwm->rows_per_field - h - 2) >> 1));
391+
BITS(VEC_FRONT_PORCH_HFP_MINUS1, hpad - 1) |
392+
BITS(VEC_FRONT_PORCH_VFP_MINUS1, vpad - 1));
355393
VEC_WRITE(VEC_MODE,
356394
BITS(VEC_MODE_HIGH_WATER, 0xE0) |
357395
BITS(VEC_MODE_ALIGN16, !((w | mode->hdisplay) & 15)) |
358-
BITS(VEC_MODE_VFP_EN, (hwm->rows_per_field > h + 1)) |
359-
BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h)) |
360-
BITS(VEC_MODE_HFP_EN, (hwm->total_cols > w + 1)) |
361-
BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w)) |
396+
BITS(VEC_MODE_VFP_EN, (vpad > 0)) |
397+
BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h + vpad)) |
398+
BITS(VEC_MODE_HFP_EN, (hpad > 0)) |
399+
BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w + hpad)) |
362400
BITS(VEC_MODE_FIELDS_PER_FRAME_MINUS1, hwm->interlaced) |
363401
BITS(VEC_MODE_FIRST_FIELD_ODD, hwm->first_field_odd));
364402
for (i = 0; i < ARRAY_SIZE(hwm->back_end_regs); ++i) {

0 commit comments

Comments
 (0)