openwrt/target/linux/bcm27xx/patches-6.6/950-1055-drm-rp1-vec-Support-more-video-modes-in-the-RP1-VEC-.patch
Álvaro Fernández Rojas 8c405cdccc bcm27xx: add 6.6 kernel patches
The patches were generated from the RPi repo with the following command:
git format-patch v6.6.34..rpi-6.1.y

Some patches needed rebasing and, as usual, the applied and reverted, wireless
drivers, Github workflows, READMEs and defconfigs patches were removed.

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2024-06-18 18:52:49 +02:00

1177 lines
43 KiB
Diff

From 4e81411f2a7fa068a49c34c87b692ba99801041b Mon Sep 17 00:00:00 2001
From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Date: Tue, 16 Apr 2024 17:30:32 +0100
Subject: [PATCH 1055/1085] drm: rp1: vec: Support more video modes in the RP1
VEC driver
Support a wider range of pixel clock rates. The driver will round
pixclock up to 108MHz/n but tries to honour the desired image width
and position (of the centre of the display relative to HSYNC_STARTs).
This adds complexity but removes the need for separate 13.5MHz and
15.428MHz modes.
Support "fake" double-rate progressive modes (in which only every
2nd scanline is displayed). To work around aspect ratio issues.
Add Monochrome TV mode support. Add "vintage" modes (544x380i for
System A; 848x738i for System E) when configured for Monochrome.
Add a way to create a "custom" display mode from a module parameter.
Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
---
drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c | 236 +++++---
drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h | 19 +-
drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c | 674 ++++++++++++-----------
3 files changed, 544 insertions(+), 385 deletions(-)
--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
+++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
@@ -48,6 +48,63 @@
#include "rp1_vec.h"
+/*
+ * Linux doesn't make it easy to create custom video modes for the console
+ * with non-CVT timings; so add a module parameter for it. The format is:
+ * "<pclk>,<hact>,<hfp>,<hsync>,<hbp>,<vact>,<vfp>,<vsync>,<vbp>[,i]"
+ * (where each comma may be replaced by any sequence of punctuation).
+ * pclk should be 108000/n for 5 <= n <= 16 (twice this for "fake" modes).
+ */
+
+static char *rp1vec_cmode_str;
+module_param_named(cmode, rp1vec_cmode_str, charp, 0600);
+MODULE_PARM_DESC(cmode, "Custom video mode:\n"
+ "\t\t<pclk>,<hact>,<hfp>,<hsync>,<hbp>,<vact>,<vfp>,<vsync>,<vbp>[,i]\n");
+
+static struct drm_display_mode *rp1vec_parse_custom_mode(struct drm_device *dev)
+{
+ char const *p = rp1vec_cmode_str;
+ struct drm_display_mode *mode;
+ unsigned int n, vals[9];
+
+ if (!p)
+ return NULL;
+
+ for (n = 0; n < 9; n++) {
+ unsigned int v = 0;
+
+ if (!isdigit(*p))
+ return NULL;
+ do {
+ v = 10u * v + (*p - '0');
+ } while (isdigit(*++p));
+
+ vals[n] = v;
+ while (ispunct(*p))
+ p++;
+ }
+
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return NULL;
+
+ mode->clock = vals[0];
+ mode->hdisplay = vals[1];
+ mode->hsync_start = mode->hdisplay + vals[2];
+ mode->hsync_end = mode->hsync_start + vals[3];
+ mode->htotal = mode->hsync_end + vals[4];
+ mode->vdisplay = vals[5];
+ mode->vsync_start = mode->vdisplay + vals[6];
+ mode->vsync_end = mode->vsync_start + vals[7];
+ mode->vtotal = mode->vsync_end + vals[8];
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+ mode->flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC;
+ if (strchr(p, 'i'))
+ mode->flags |= DRM_MODE_FLAG_INTERLACE;
+
+ return mode;
+}
+
static void rp1vec_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
@@ -158,63 +215,143 @@ static void rp1vec_connector_destroy(str
drm_connector_cleanup(connector);
}
-static const struct drm_display_mode rp1vec_modes[4] = {
+/*
+ * Check the mode roughly matches something we can generate.
+ * The choice of hardware TV mode depends on total lines and frame rate.
+ * Within each hardware mode, allow pixel clock, image size and offsets
+ * to vary, up to a maximum horizontal active period and line count.
+ * Don't check sync timings here: the HW driver will sanitize them.
+ */
+
+static enum drm_mode_status rp1vec_mode_valid(struct drm_device *dev,
+ const struct drm_display_mode *mode)
+{
+ int prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
+ int fake_31khz = prog && mode->vtotal >= 500;
+ int vtotal_2fld = mode->vtotal << (prog && !fake_31khz);
+ int vdisplay_2fld = mode->vdisplay << (prog && !fake_31khz);
+ int real_clock = mode->clock >> fake_31khz;
+
+ /* Check pixel clock is in the permitted range */
+ if (real_clock < 6750)
+ return MODE_CLOCK_LOW;
+ else if (real_clock > 21600)
+ return MODE_CLOCK_HIGH;
+
+ /* Try to match against the 525-line 60Hz mode (System M) */
+ if (vtotal_2fld >= 524 && vtotal_2fld <= 526 && vdisplay_2fld <= 486 &&
+ mode->htotal * vtotal_2fld > 32 * real_clock &&
+ mode->htotal * vtotal_2fld < 34 * real_clock &&
+ 37 * mode->hdisplay <= 2 * real_clock) /* 54us */
+ return MODE_OK;
+
+ /* All other supported TV Systems (625-, 405-, 819-line) are 50Hz */
+ if (mode->htotal * vtotal_2fld > 39 * real_clock &&
+ mode->htotal * vtotal_2fld < 41 * real_clock) {
+ if (vtotal_2fld >= 624 && vtotal_2fld <= 626 && vdisplay_2fld <= 576 &&
+ 37 * mode->hdisplay <= 2 * real_clock) /* 54us */
+ return MODE_OK;
+
+ if (vtotal_2fld == 405 && vdisplay_2fld <= 380 &&
+ 49 * mode->hdisplay <= 4 * real_clock) /* 81.6us */
+ return MODE_OK;
+
+ if (vtotal_2fld == 819 && vdisplay_2fld <= 738 &&
+ 25 * mode->hdisplay <= real_clock) /* 40us */
+ return MODE_OK;
+ }
+
+ return MODE_BAD;
+}
+
+static const struct drm_display_mode rp1vec_modes[6] = {
{ /* Full size 525/60i with Rec.601 pixel rate */
DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
720, 720 + 16, 720 + 16 + 64, 858, 0,
480, 480 + 6, 480 + 6 + 6, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE)
},
{ /* Cropped and horizontally squashed to be TV-safe */
DRM_MODE("704x432i", DRM_MODE_TYPE_DRIVER, 15429,
704, 704 + 76, 704 + 76 + 72, 980, 0,
432, 432 + 30, 432 + 30 + 6, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE)
},
{ /* Full size 625/50i with Rec.601 pixel rate */
DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
720, 720 + 12, 720 + 12 + 64, 864, 0,
576, 576 + 5, 576 + 5 + 5, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE)
},
{ /* Cropped and squashed, for square(ish) pixels */
DRM_MODE("704x512i", DRM_MODE_TYPE_DRIVER, 15429,
704, 704 + 72, 704 + 72 + 72, 987, 0,
512, 512 + 37, 512 + 37 + 5, 625, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE)
+ },
+ { /* System A (405 lines) */
+ DRM_MODE("544x380i", DRM_MODE_TYPE_DRIVER, 6750,
+ 544, 544 + 12, 544 + 12 + 60, 667, 0,
+ 380, 380 + 0, 380 + 0 + 8, 405, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
+ DRM_MODE_FLAG_INTERLACE)
+ },
+ { /* System E (819 lines) */
+ DRM_MODE("848x738i", DRM_MODE_TYPE_DRIVER, 21600,
+ 848, 848 + 12, 848 + 12 + 54, 1055, 0,
+ 738, 738 + 6, 738 + 6 + 1, 819, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE)
}
};
/*
- * Advertise standard and preferred video modes.
+ * Advertise a custom mode, if specified; then those from the table above.
+ * From each interlaced mode above, derive a half-height progressive one.
*
- * From each interlaced mode in the table above, derive a progressive one.
+ * This driver always supports all 525-line and 625-line standard modes
+ * regardless of connector's tv_mode; non-standard combinations generally
+ * default to PAL[-BDGHIK] or NTSC[-M] (with a special case for "PAL60").
*
- * This driver always supports all 50Hz and 60Hz video modes, regardless
- * of connector's tv_mode; nonstandard combinations generally default
- * to PAL[-BDGHIKL] or NTSC[-M] depending on resolution and field-rate
- * (except that "PAL" with 525/60 will be implemented as "PAL60").
- * However, the preferred mode will depend on the default TV mode.
+ * The "vintage" standards (System A, System E) are advertised only when
+ * the default tv_mode was DRM_MODE_TV_MODE_MONOCHROME, and only interlaced.
*/
static int rp1vec_connector_get_modes(struct drm_connector *connector)
{
- u64 val;
- int i, prog, n = 0;
- bool prefer625 = false;
+ u64 tvstd;
+ int i, prog, limit, n = 0, preferred_lines = 525;
+ struct drm_display_mode *mode;
if (!drm_object_property_get_default_value(&connector->base,
connector->dev->mode_config.tv_mode_property,
- &val))
- prefer625 = (val == DRM_MODE_TV_MODE_PAL ||
- val == DRM_MODE_TV_MODE_PAL_N ||
- val == DRM_MODE_TV_MODE_SECAM);
+ &tvstd))
+ preferred_lines = (tvstd == DRM_MODE_TV_MODE_PAL ||
+ tvstd == DRM_MODE_TV_MODE_PAL_N ||
+ tvstd >= DRM_MODE_TV_MODE_SECAM) ? 625 : 525;
+
+ mode = rp1vec_parse_custom_mode(connector->dev);
+ if (mode) {
+ if (rp1vec_mode_valid(connector->dev, mode) == 0) {
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+ n++;
+ preferred_lines = 0;
+ } else {
+ drm_mode_destroy(connector->dev, mode);
+ }
+ }
- for (i = 0; i < ARRAY_SIZE(rp1vec_modes); i++) {
+ limit = (tvstd < DRM_MODE_TV_MODE_MONOCHROME) ? 4 : ARRAY_SIZE(rp1vec_modes);
+ for (i = 0; i < limit; i++) {
for (prog = 0; prog < 2; prog++) {
- struct drm_display_mode *mode =
- drm_mode_duplicate(connector->dev,
- &rp1vec_modes[i]);
+ mode = drm_mode_duplicate(connector->dev, &rp1vec_modes[i]);
+ if (!mode)
+ return n;
if (prog) {
mode->flags &= ~DRM_MODE_FLAG_INTERLACE;
@@ -222,15 +359,15 @@ static int rp1vec_connector_get_modes(st
mode->vsync_start >>= 1;
mode->vsync_end >>= 1;
mode->vtotal >>= 1;
- }
-
- if (mode->hdisplay == 704 &&
- mode->vtotal == (prefer625 ? 625 : 525))
+ } else if (mode->hdisplay == 704 && mode->vtotal == preferred_lines) {
mode->type |= DRM_MODE_TYPE_PREFERRED;
-
+ }
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
n++;
+
+ if (mode->vtotal == 405 || mode->vtotal == 819)
+ break; /* Don't offer progressive for Systems A, E */
}
}
@@ -260,49 +397,6 @@ static int rp1vec_connector_atomic_check
return 0;
}
-static enum drm_mode_status rp1vec_mode_valid(struct drm_device *dev,
- const struct drm_display_mode *mode)
-{
- /*
- * Check the mode roughly matches something we can generate.
- * The hardware driver is very prescriptive about pixel clocks,
- * line and frame durations, but we'll tolerate rounding errors.
- * Within each hardware mode, allow image size and position to vary
- * (to fine-tune overscan correction or emulate retro devices).
- * Don't check sync timings here: the HW driver will sanitize them.
- */
-
- int prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
- int vtotal_full = mode->vtotal << prog;
- int vdisplay_full = mode->vdisplay << prog;
-
- /* Reject very small frames */
- if (vtotal_full < 256 || mode->hdisplay < 256)
- return MODE_BAD;
-
- /* Check lines, frame period (ms) and vertical size limit */
- if (vtotal_full >= 524 && vtotal_full <= 526 &&
- mode->htotal * vtotal_full > 33 * mode->clock &&
- mode->htotal * vtotal_full < 34 * mode->clock &&
- vdisplay_full <= 480)
- goto vgood;
- if (vtotal_full >= 624 && vtotal_full <= 626 &&
- mode->htotal * vtotal_full > 39 * mode->clock &&
- mode->htotal * vtotal_full < 41 * mode->clock &&
- vdisplay_full <= 576)
- goto vgood;
- return MODE_BAD;
-
-vgood:
- /* Check pixel rate (kHz) and horizontal size limit */
- if (mode->clock == 13500 && mode->hdisplay <= 720)
- return MODE_OK;
- if (mode->clock >= 15428 && mode->clock <= 15429 &&
- mode->hdisplay <= 800)
- return MODE_OK;
- return MODE_BAD;
-}
-
static const struct drm_connector_helper_funcs rp1vec_connector_helper_funcs = {
.get_modes = rp1vec_connector_get_modes,
.atomic_check = rp1vec_connector_atomic_check,
@@ -410,10 +504,12 @@ static int rp1vec_platform_probe(struct
vec->drm.dev_private = vec;
platform_set_drvdata(pdev, &vec->drm);
- vec->drm.mode_config.max_width = 800;
- vec->drm.mode_config.max_height = 576;
+ vec->drm.mode_config.min_width = 256;
+ vec->drm.mode_config.min_height = 128;
+ vec->drm.mode_config.max_width = 848; /* for System E */
+ vec->drm.mode_config.max_height = 738; /* for System E */
vec->drm.mode_config.preferred_depth = 32;
- vec->drm.mode_config.prefer_shadow = 0;
+ vec->drm.mode_config.prefer_shadow = 0;
vec->drm.mode_config.quirk_addfb_prefer_host_byte_order = true;
vec->drm.mode_config.funcs = &rp1vec_mode_funcs;
drm_vblank_init(&vec->drm, 1);
--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h
+++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h
@@ -21,12 +21,15 @@
#define RP1VEC_NUM_HW_BLOCKS 2
#define RP1VEC_SUPPORTED_TV_MODES \
- (BIT(DRM_MODE_TV_MODE_NTSC) | \
+ (BIT(DRM_MODE_TV_MODE_NTSC) | \
BIT(DRM_MODE_TV_MODE_NTSC_443) | \
- BIT(DRM_MODE_TV_MODE_NTSC_J) | \
- BIT(DRM_MODE_TV_MODE_PAL) | \
- BIT(DRM_MODE_TV_MODE_PAL_M) | \
- BIT(DRM_MODE_TV_MODE_PAL_N))
+ BIT(DRM_MODE_TV_MODE_NTSC_J) | \
+ BIT(DRM_MODE_TV_MODE_PAL) | \
+ BIT(DRM_MODE_TV_MODE_PAL_M) | \
+ BIT(DRM_MODE_TV_MODE_PAL_N) | \
+ BIT(DRM_MODE_TV_MODE_MONOCHROME))
+
+#define RP1VEC_VDAC_KHZ 108000
/* ---------------------------------------------------------------------- */
@@ -45,7 +48,7 @@ struct rp1_vec {
/* Block (VCC, CFG) base addresses, and current state */
void __iomem *hw_base[RP1VEC_NUM_HW_BLOCKS];
u32 cur_fmt;
- bool vec_running, pipe_enabled;
+ bool fake_31khz, vec_running, pipe_enabled;
struct completion finished;
};
@@ -54,8 +57,8 @@ struct rp1_vec {
void rp1vec_hw_setup(struct rp1_vec *vec,
u32 in_format,
- struct drm_display_mode const *mode,
- int tvstd);
+ struct drm_display_mode const *mode,
+ int tvstd);
void rp1vec_hw_update(struct rp1_vec *vec, dma_addr_t addr, u32 offset, u32 stride);
void rp1vec_hw_stop(struct rp1_vec *vec);
int rp1vec_hw_busy(struct rp1_vec *vec);
--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
+++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
@@ -18,11 +18,18 @@
#include "rp1_vec.h"
#include "vec_regs.h"
-#define BITS(field, val) (((val) << (field ## _LSB)) & (field ## _BITS))
-
+#define BITS(field, val) (((val) << (field ## _LSB)) & (field ## _BITS))
#define VEC_WRITE(reg, val) writel((val), vec->hw_base[RP1VEC_HW_BLOCK_VEC] + (reg ## _OFFSET))
#define VEC_READ(reg) readl(vec->hw_base[RP1VEC_HW_BLOCK_VEC] + (reg ## _OFFSET))
+static void rp1vec_write_regs(struct rp1_vec *vec, u32 offset, u32 const *vals, u32 num)
+{
+ while (num--) {
+ writel(*vals++, vec->hw_base[RP1VEC_HW_BLOCK_VEC] + offset);
+ offset += 4;
+ }
+}
+
int rp1vec_hw_busy(struct rp1_vec *vec)
{
/* Read the undocumented "pline_busy" flag */
@@ -46,32 +53,32 @@ static const struct rp1vec_ipixfmt my_fo
{
.format = DRM_FORMAT_XRGB8888,
.mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
- .shift = SHIFT_RGB(23, 15, 7),
- .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
+ .shift = SHIFT_RGB(23, 15, 7),
+ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
},
{
.format = DRM_FORMAT_XBGR8888,
.mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
- .shift = SHIFT_RGB(7, 15, 23),
- .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
+ .shift = SHIFT_RGB(7, 15, 23),
+ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
},
{
.format = DRM_FORMAT_RGB888,
.mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
- .shift = SHIFT_RGB(23, 15, 7),
- .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 2),
+ .shift = SHIFT_RGB(23, 15, 7),
+ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 2),
},
{
.format = DRM_FORMAT_BGR888,
.mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
- .shift = SHIFT_RGB(7, 15, 23),
- .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 2),
+ .shift = SHIFT_RGB(7, 15, 23),
+ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 2),
},
{
.format = DRM_FORMAT_RGB565,
.mask = MASK_RGB(0x3e0, 0x3f0, 0x3e0),
- .shift = SHIFT_RGB(15, 10, 4),
- .rgbsz = BITS(VEC_RGBSZ_SCALE_R, 5) |
+ .shift = SHIFT_RGB(15, 10, 4),
+ .rgbsz = BITS(VEC_RGBSZ_SCALE_R, 5) |
BITS(VEC_RGBSZ_SCALE_G, 6) |
BITS(VEC_RGBSZ_SCALE_B, 5) |
BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 1),
@@ -79,301 +86,338 @@ static const struct rp1vec_ipixfmt my_fo
};
/*
- * Hardware mode descriptions (@ 108 MHz clock rate).
- * These rely largely on "canned" register settings.
+ * Hardware mode descriptions (@ 108 MHz VDAC clock)
+ * 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.
*/
struct rp1vec_hwmode {
- u16 total_cols; /* max active columns incl. padding and windowing */
- u16 rows_per_field; /* active lines per field (including partial ones) */
- u16 ref_hfp; /* nominal (hsync_start - hdisplay) when max width */
- u16 ref_vfp; /* nominal (vsync_start - vdisplay) when max height */
- bool interlaced; /* set for interlaced */
- bool first_field_odd; /* set for interlaced and 30fps */
- u32 yuv_scaling; /* three 10-bit fields {Y, U, V} in 2.8 format */
- u32 back_end_regs[28]; /* All registers 0x80 .. 0xEC */
+ 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 */
+ 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) */
+ u16 de_end; /* end of horizontal Data Active period at 108MHz */
+ u16 de_bgn; /* start of horizontal Data Active period */
+ u16 half_lines_per_field; /* number of half lines per field */
+ s16 pedestal; /* pedestal (1024 = 100IRE) including FIR overshoot */
+ u16 scale_luma; /* back end luma scaling in 1.15 format wrt DAC FSD */
+ u16 scale_sync; /* back end sync scaling / blanking level as above */
+ u32 scale_burst_chroma; /* back end { burst, chroma } scaling */
+ u32 misc; /* Contents of the "EC" register except rate,shift */
+ u64 nco_freq; /* colour carrier frequency * (2**64) / 108MHz */
+ u32 timing_regs[14]; /* other back end registers 0x84 .. 0xB8 */
};
-/* { NTSC, PAL, PAL-M } x { progressive, interlaced } x { 13.5 MHz, 15.428571 MHz } */
-static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
+/* { NTSC, PAL, PAL-M } x { progressive, interlaced } */
+static const struct rp1vec_hwmode rp1vec_hwmodes[3][2] = {
{
/* NTSC */
{
- {
- .total_cols = 724,
- .rows_per_field = 240,
- .ref_hfp = 12,
- .ref_vfp = 2,
- .interlaced = false,
- .first_field_odd = false,
- .yuv_scaling = 0x1071d0cf,
- .back_end_regs = {
- 0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
- 0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
- 0x000a0106, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00170106, 0x00000000, 0x004c020e,
- 0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
- 0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ec,
- },
- }, {
- .total_cols = 815,
- .rows_per_field = 240,
- .ref_hfp = 16,
- .ref_vfp = 2,
- .interlaced = false,
- .first_field_odd = false,
- .yuv_scaling = 0x1c131962,
- .back_end_regs = {
- 0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
- 0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
- 0x000a0106, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00170106, 0x00000000, 0x004c020e,
- 0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
- 0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ac,
- },
+ .max_rows_per_field = 240,
+ .ref_vfp = 2,
+ .interlaced = false,
+ .first_field_odd = false,
+ .scale_v = 0x0cf,
+ .scale_u = 0x074,
+ .scale_y = 0x107,
+ .de_end = 0x1a4f,
+ .de_bgn = 0x038f,
+ .half_lines_per_field = 524, /* also works with 526/2 lines */
+ .pedestal = 0x04c,
+ .scale_luma = 0x8c9a,
+ .scale_sync = 0x3851,
+ .scale_burst_chroma = 0x11195561,
+ .misc = 0x00090c00, /* 5-tap FIR, SEQ_EN, 4 fld sync */
+ .nco_freq = 0x087c1f07c1f07c1f,
+ .timing_regs = {
+ 0x03e10cc6, 0x0d6801fb, 0x023d034c, 0x00f80b6d,
+ 0x00000005, 0x0006000b, 0x000c0011, 0x000a0106,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00170106, 0x00000000
},
}, {
- {
- .total_cols = 724,
- .rows_per_field = 243,
- .ref_hfp = 12,
- .ref_vfp = 3,
- .interlaced = true,
- .first_field_odd = true,
- .yuv_scaling = 0x1071d0cf,
- .back_end_regs = {
- 0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
- 0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
- 0x000a0107, 0x0111020d, 0x00000000, 0x00000000,
- 0x011c020d, 0x00150106, 0x0107011b, 0x004c020d,
- 0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
- 0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x00094dee,
- },
- }, {
- .total_cols = 815,
- .rows_per_field = 243,
- .ref_hfp = 16,
- .ref_vfp = 3,
- .interlaced = true,
- .first_field_odd = true,
- .yuv_scaling = 0x1c131962,
- .back_end_regs = {
- 0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
- 0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
- 0x000a0107, 0x0111020d, 0x00000000, 0x00000000,
- 0x011c020d, 0x00150106, 0x0107011b, 0x004c020d,
- 0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
- 0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x00094dae,
- },
+ .max_rows_per_field = 243,
+ .ref_vfp = 3,
+ .interlaced = true,
+ .first_field_odd = true,
+ .scale_v = 0x0cf,
+ .scale_u = 0x074,
+ .scale_y = 0x107,
+ .de_end = 0x1a4f,
+ .de_bgn = 0x038f,
+ .half_lines_per_field = 525,
+ .pedestal = 0x04c,
+ .scale_luma = 0x8c9a,
+ .scale_sync = 0x3851,
+ .scale_burst_chroma = 0x11195561,
+ .misc = 0x00094c02, /* 5-tap FIR, SEQ_EN, 2 flds, 4 fld sync, ilace */
+ .nco_freq = 0x087c1f07c1f07c1f,
+ .timing_regs = {
+ 0x03e10cc6, 0x0d6801fb, 0x023d034c, 0x00f80b6d,
+ 0x00000005, 0x0006000b, 0x000c0011, 0x000a0107,
+ 0x0111020d, 0x00000000, 0x00000000, 0x011c020d,
+ 0x00150106, 0x0107011b,
},
},
}, {
/* PAL */
{
- {
- .total_cols = 724,
- .rows_per_field = 288,
- .ref_hfp = 16,
- .ref_vfp = 2,
- .interlaced = false,
- .first_field_odd = false,
- .yuv_scaling = 0x11c1f8e0,
- .back_end_regs = {
- 0x04061aa6, 0x046e0cee, 0x0d8001fb, 0x025c034f,
- 0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
- 0x00070135, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00170136, 0x00000000, 0x000a0270,
- 0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
- 0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ed,
- },
- }, {
- .total_cols = 804,
- .rows_per_field = 288,
- .ref_hfp = 24,
- .ref_vfp = 2,
- .interlaced = false,
- .first_field_odd = false,
- .yuv_scaling = 0x1e635d7f,
- .back_end_regs = {
- 0x045b1a57, 0x046e0cee, 0x0d8001fb, 0x025c034f,
- 0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
- 0x00070135, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00170136, 0x00000000, 0x000a0270,
- 0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
- 0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ad,
- },
+ .max_rows_per_field = 288,
+ .ref_vfp = 2,
+ .interlaced = false,
+ .first_field_odd = false,
+ .scale_v = 0x0e0,
+ .scale_u = 0x07e,
+ .scale_y = 0x11c,
+ .de_end = 0x1ab6,
+ .de_bgn = 0x03f6,
+ .half_lines_per_field = 624,
+ .pedestal = 0x00a, /* nonzero for max FIR overshoot after CIC */
+ .scale_luma = 0x89d8,
+ .scale_sync = 0x3c00,
+ .scale_burst_chroma = 0x0caf53b5,
+ .misc = 0x00091c01, /* 5-tap FIR, SEQ_EN, 8 fld sync, PAL */
+ .nco_freq = 0x0a8262b2cc48c1d1,
+ .timing_regs = {
+ 0x046e0cee, 0x0d8001fb, 0x025c034f, 0x00fd0b84,
+ 0x026c0270, 0x00000004, 0x00050009, 0x00070135,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00170136, 0x00000000,
},
}, {
- {
- .total_cols = 724,
- .rows_per_field = 288,
- .ref_hfp = 16,
- .ref_vfp = 5,
- .interlaced = true,
- .first_field_odd = false,
- .yuv_scaling = 0x11c1f8e0,
- .back_end_regs = {
- 0x04061aa6, 0x046e0cee, 0x0d8001fb, 0x025c034f,
- 0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
- 0x00070135, 0x013f026d, 0x00060136, 0x0140026e,
- 0x0150026e, 0x00180136, 0x026f0017, 0x000a0271,
- 0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
- 0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddef,
- },
- }, {
- .total_cols = 804,
- .rows_per_field = 288,
- .ref_hfp = 24,
- .ref_vfp = 5,
- .interlaced = true,
- .first_field_odd = false,
- .yuv_scaling = 0x1e635d7f,
- .back_end_regs = {
- 0x045b1a57, 0x046e0cee, 0x0d8001fb, 0x025c034f,
- 0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
- 0x00070135, 0x013f026d, 0x00060136, 0x0140026e,
- 0x0150026e, 0x00180136, 0x026f0017, 0x000a0271,
- 0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
- 0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddaf,
- },
+ .max_rows_per_field = 288,
+ .ref_vfp = 5,
+ .interlaced = true,
+ .first_field_odd = false,
+ .scale_v = 0x0e0,
+ .scale_u = 0x07e,
+ .scale_y = 0x11c,
+ .de_end = 0x1ab6,
+ .de_bgn = 0x03f6,
+ .half_lines_per_field = 625,
+ .pedestal = 0x00a,
+ .scale_luma = 0x89d8,
+ .scale_sync = 0x3c00,
+ .scale_burst_chroma = 0x0caf53b5,
+ .misc = 0x0009dc03, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, ilace, PAL */
+ .nco_freq = 0x0a8262b2cc48c1d1,
+ .timing_regs = {
+ 0x046e0cee, 0x0d8001fb, 0x025c034f, 0x00fd0b84,
+ 0x026c0270, 0x00000004, 0x00050009, 0x00070135,
+ 0x013f026d, 0x00060136, 0x0140026e, 0x0150026e,
+ 0x00180136, 0x026f0017,
},
},
}, {
/* PAL-M */
{
- {
- .total_cols = 724,
- .rows_per_field = 240,
- .ref_hfp = 12,
- .ref_vfp = 2,
- .interlaced = false,
- .first_field_odd = false,
- .yuv_scaling = 0x11c1f8e0,
- .back_end_regs = {
- 0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
- 0x00f80b6e, 0x00000005, 0x0006000b, 0x000c0011,
- 0x000a0106, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00170106, 0x00000000, 0x000a020c,
- 0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
- 0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ed,
- },
- }, {
- .total_cols = 815,
- .rows_per_field = 240,
- .ref_hfp = 16,
- .ref_vfp = 2,
- .interlaced = false,
- .first_field_odd = false,
- .yuv_scaling = 0x1e635d7f,
- .back_end_regs = {
- 0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
- 0x00f80b6e, 0x00000005, 0x0006000b, 0x000c0011,
- 0x000a0106, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00170106, 0x00000000, 0x000a020c,
- 0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
- 0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ad,
- },
+ .max_rows_per_field = 240,
+ .ref_vfp = 2,
+ .interlaced = false,
+ .first_field_odd = false,
+ .scale_v = 0x0e0,
+ .scale_u = 0x07e,
+ .scale_y = 0x11c,
+ .de_end = 0x1a4f,
+ .de_bgn = 0x038f,
+ .half_lines_per_field = 524,
+ .pedestal = 0x00a,
+ .scale_luma = 0x89d8,
+ .scale_sync = 0x3851,
+ .scale_burst_chroma = 0x0d5c53b5,
+ .misc = 0x00091c01, /* 5-tap FIR, SEQ_EN, 8 fld sync PAL */
+ .nco_freq = 0x0879bbf8d6d33ea8,
+ .timing_regs = {
+ 0x03e10cc6, 0x0d6801fb, 0x023c034c, 0x00f80b6e,
+ 0x00000005, 0x0006000b, 0x000c0011, 0x000a0106,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00170106, 0x00000000,
},
}, {
- {
- .total_cols = 724,
- .rows_per_field = 243,
- .ref_hfp = 12,
- .ref_vfp = 3,
- .interlaced = true,
- .first_field_odd = true,
- .yuv_scaling = 0x11c1f8e0,
- .back_end_regs = {
- 0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
- 0x00f80b6e, 0x00140019, 0x00000005, 0x0006000b,
- 0x00090103, 0x010f0209, 0x00080102, 0x010e020a,
- 0x0119020a, 0x00120103, 0x01040118, 0x000a020d,
- 0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
- 0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddef,
- },
- }, {
- .total_cols = 815,
- .rows_per_field = 243,
- .ref_hfp = 16,
- .ref_vfp = 3,
- .interlaced = true,
- .first_field_odd = true,
- .yuv_scaling = 0x1e635d7f,
- .back_end_regs = {
- 0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
- 0x00f80b6e, 0x00140019, 0x00000005, 0x0006000b,
- 0x00090103, 0x010f0209, 0x00080102, 0x010e020a,
- 0x0119020a, 0x00120103, 0x01040118, 0x000a020d,
- 0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
- 0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
- 0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddaf,
- },
+ .max_rows_per_field = 243,
+ .ref_vfp = 3,
+ .interlaced = true,
+ .first_field_odd = true,
+ .scale_v = 0x0e0,
+ .scale_u = 0x07e,
+ .scale_y = 0x11c,
+ .de_end = 0x1a4f,
+ .de_bgn = 0x038f,
+ .half_lines_per_field = 525,
+ .pedestal = 0x00a,
+ .scale_luma = 0x89d8,
+ .scale_sync = 0x3851,
+ .scale_burst_chroma = 0x0d5c53b5,
+ .misc = 0x0009dc03, /* 5-tap FIR, SEQ_EN, 4 flds, 8 fld sync, ilace, PAL */
+ .nco_freq = 0x0879bbf8d6d33ea8,
+ .timing_regs = {
+ 0x03e10cc6, 0x0d6801fb, 0x023c034c, 0x00f80b6e,
+ 0x00140019, 0x00000005, 0x0006000b, 0x00090103,
+ 0x010f0209, 0x00080102, 0x010e020a, 0x0119020a,
+ 0x00120103, 0x01040118,
},
},
},
};
+/* System A, System E */
+static const struct rp1vec_hwmode rp1vec_vintage_modes[2] = {
+ {
+ .max_rows_per_field = 190,
+ .ref_vfp = 0,
+ .interlaced = true,
+ .first_field_odd = true,
+ .scale_v = 0,
+ .scale_u = 0,
+ .scale_y = 0x11c,
+ .de_end = 0x2920,
+ .de_bgn = 0x06a0,
+ .half_lines_per_field = 405,
+ .pedestal = 0x00a,
+ .scale_luma = 0x89d8,
+ .scale_sync = 0x3c00,
+ .scale_burst_chroma = 0,
+ .misc = 0x00084002, /* 5-tap FIR, 2 fields, interlace */
+ .nco_freq = 0,
+ .timing_regs = {
+ 0x06f01430, 0x14d503cc, 0x00000000, 0x000010de,
+ 0x00000000, 0x00000007, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00d90195,
+ 0x000e00ca, 0x00cb00d8,
+ },
+ }, {
+ .max_rows_per_field = 369,
+ .ref_vfp = 6,
+ .interlaced = true,
+ .first_field_odd = true,
+ .scale_v = 0,
+ .scale_u = 0,
+ .scale_y = 0x11c,
+ .de_end = 0x145f,
+ .de_bgn = 0x03a7,
+ .half_lines_per_field = 819,
+ .pedestal = 0x0010,
+ .scale_luma = 0x89d8,
+ .scale_sync = 0x3b13,
+ .scale_burst_chroma = 0,
+ .misc = 0x00084002, /* 5-tap FIR, 2 fields, interlace */
+ .nco_freq = 0,
+ .timing_regs = {
+ 0x03c10a08, 0x0a4d0114, 0x00000000, 0x000008a6,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x01c10330,
+ 0x00270196, 0x019701c0,
+ },
+ },
+};
+
+static const u32 rp1vec_fir_regs[4] = {
+ 0x00000000, 0x0be20200, 0x20f0f800, 0x265c7f00,
+};
+
+/*
+ * Correction for the 4th order CIC filter's gain of (rate ** 4)
+ * expressed as a right-shift and a reciprocal scale factor (Q12).
+ * These arrays are indexed by [rate - 4] where 4 <= rate <= 16.
+ */
+
+static const int rp1vec_scale_table[13] = {
+ 4096, 6711, 6473, 6988,
+ 4096, 5114, 6711, 4584,
+ 6473, 4699, 6988, 5302,
+ 4096
+};
+
+static const u32 rp1vec_rate_shift_table[13] = {
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 3) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 7),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 4) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 9),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 5) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 10),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 6) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 11),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 7) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 11),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 8) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 12),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 9) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 13),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 10) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 13),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 11) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 14),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 12) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 14),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 13) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 15),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 14) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 15),
+ BITS(VEC_DAC_EC_INTERP_RATE_MINUS1, 15) | BITS(VEC_DAC_EC_INTERP_SHIFT_MINUS1, 15),
+};
+
void rp1vec_hw_setup(struct rp1_vec *vec,
u32 in_format,
struct drm_display_mode const *mode,
int tvstd)
{
- unsigned int i, mode_family, mode_ilaced, mode_narrow;
+ int i, mode_family, w, h;
const struct rp1vec_hwmode *hwm;
- int w, h, hpad, vpad;
+ int wmax, hpad_r, vpad_b, rate, ref_2mid, usr_2mid;
+ u32 misc;
- /* Pick the appropriate "base" mode, which we may modify */
- mode_ilaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
- if (mode->vtotal >= 272 * (1 + mode_ilaced))
+ /* Input pixel format conversion */
+ for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
+ if (my_formats[i].format == in_format)
+ break;
+ }
+ if (i >= ARRAY_SIZE(my_formats)) {
+ dev_err(&vec->pdev->dev, "%s: bad input format\n", __func__);
+ i = 0;
+ }
+ VEC_WRITE(VEC_IMASK, my_formats[i].mask);
+ VEC_WRITE(VEC_SHIFT, my_formats[i].shift);
+ VEC_WRITE(VEC_RGBSZ, my_formats[i].rgbsz);
+
+ /* Pick an appropriate "base" mode, which we may modify.
+ * Note that this driver supports a limited selection of video modes.
+ * (A complete TV mode cannot be directly inferred from a DRM display mode:
+ * features such as chroma burst sequence, half-lines and equalizing pulses
+ * would be under-specified, and timings prone to rounding errors.)
+ */
+ if (mode->vtotal == 405 || mode->vtotal == 819) {
+ /* Systems A and E (interlaced only) */
+ vec->fake_31khz = false;
mode_family = 1;
- else if (tvstd == DRM_MODE_TV_MODE_PAL_M || tvstd == DRM_MODE_TV_MODE_PAL)
- mode_family = 2;
- else
- mode_family = 0;
- mode_narrow = (mode->clock >= 14336);
- hwm = &rp1vec_hwmodes[mode_family][mode_ilaced][mode_narrow];
- dev_info(&vec->pdev->dev,
- "%s: in_fmt=\'%c%c%c%c\' mode=%dx%d%s [%d%d%d] tvstd=%d",
- __func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24,
- mode->hdisplay, mode->vdisplay, (mode_ilaced) ? "i" : "",
- mode_family, mode_ilaced, mode_narrow, tvstd);
-
- w = mode->hdisplay;
- h = mode->vdisplay >> mode_ilaced;
- if (w > hwm->total_cols)
- w = hwm->total_cols;
- if (h > hwm->rows_per_field)
- h = hwm->rows_per_field;
+ hwm = &rp1vec_vintage_modes[(mode->vtotal == 819) ? 1 : 0];
+ } else {
+ /* 525- and 625-line modes, with half-height and "fake" progressive variants */
+ vec->fake_31khz = mode->vtotal >= 500 && !(mode->flags & DRM_MODE_FLAG_INTERLACE);
+ h = (mode->vtotal >= 500) ? (mode->vtotal >> 1) : mode->vtotal;
+ if (h >= 272)
+ mode_family = 1; /* PAL-625 */
+ else if (tvstd == DRM_MODE_TV_MODE_PAL_M || tvstd == DRM_MODE_TV_MODE_PAL)
+ mode_family = 2; /* PAL-525 */
+ else
+ mode_family = 0; /* NTSC-525 */
+ hwm = &rp1vec_hwmodes[mode_family][(mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0];
+ }
/*
- * Add padding so a framebuffer with the given dimensions and
- * [hv]sync_start can be displayed in the chosen hardware mode.
- *
- * |<----- mode->hsync_start ----->|
- * |<------ w ------>| |
- * | | >|--|< ref_hfp
- * |<- hpad ->|
- * |<------------ total_cols ----------->|
- * ________FRAMEBUFFERCONTENTS__________
- * ' `--\____/-<\/\/\>-'
+ * Choose the upsampling rate (to 108MHz) in the range 4..16.
+ * Clip dimensions to the limits of the chosen hardware mode, then add
+ * padding as required, making some attempt to respect the DRM mode's
+ * display position (relative to H and V sync start). Note that "wmax"
+ * should be wider than the horizontal active region, to avoid boundary
+ * artifacts (e.g. wmax = 728, w = 720, active ~= 704 in Rec.601 modes).
*/
- hpad = max(0, mode->hsync_start - hwm->ref_hfp - w);
- hpad = min(hpad, hwm->total_cols - w);
- vpad = max(0, ((mode->vsync_start - hwm->ref_vfp) >> mode_ilaced) - h);
- vpad = min(vpad, hwm->rows_per_field - h);
+ i = (vec->fake_31khz) ? (mode->clock >> 1) : mode->clock;
+ rate = (i < (RP1VEC_VDAC_KHZ / 16)) ? 16 : max(4, (RP1VEC_VDAC_KHZ + 256) / i);
+ wmax = min((hwm->de_end - hwm->de_bgn) / rate, 1020);
+ w = min(mode->hdisplay, wmax);
+ ref_2mid = (hwm->de_bgn + hwm->de_end) / rate + 4; /* + 4 for FIR delay */
+ usr_2mid = (2 * (mode->htotal - mode->hsync_start) + w) * 2 * (hwm->timing_regs[1] >> 16) /
+ (rate * mode->htotal);
+ hpad_r = (wmax - w + ref_2mid - usr_2mid) >> 1;
+ hpad_r = min(max(0, hpad_r), wmax - w);
+ h = mode->vdisplay >> (hwm->interlaced || vec->fake_31khz);
+ h = min(h, 0 + hwm->max_rows_per_field);
+ 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 */
+ /* Configure the hardware "front end" (in the sysclock domain) */
VEC_WRITE(VEC_APB_TIMEOUT, 0x38);
VEC_WRITE(VEC_QOS,
BITS(VEC_QOS_DQOS, 0x0) |
@@ -384,66 +428,78 @@ void rp1vec_hw_setup(struct rp1_vec *vec
VEC_WRITE(VEC_DMA_AREA,
BITS(VEC_DMA_AREA_COLS_MINUS1, w - 1) |
BITS(VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1, h - 1));
- VEC_WRITE(VEC_YUV_SCALING, hwm->yuv_scaling);
+ VEC_WRITE(VEC_YUV_SCALING,
+ BITS(VEC_YUV_SCALING_U10_SCALE_Y,
+ (hwm->scale_y * rp1vec_scale_table[rate - 4] + 2048) >> 12) |
+ BITS(VEC_YUV_SCALING_S10_SCALE_U,
+ (hwm->scale_u * rp1vec_scale_table[rate - 4] + 2048) >> 12) |
+ BITS(VEC_YUV_SCALING_S10_SCALE_V,
+ (hwm->scale_v * rp1vec_scale_table[rate - 4] + 2048) >> 12));
VEC_WRITE(VEC_BACK_PORCH,
- BITS(VEC_BACK_PORCH_HBP_MINUS1, hwm->total_cols - w - hpad - 1) |
- BITS(VEC_BACK_PORCH_VBP_MINUS1, hwm->rows_per_field - h - vpad - 1));
+ BITS(VEC_BACK_PORCH_HBP_MINUS1, wmax - w - hpad_r - 1) |
+ BITS(VEC_BACK_PORCH_VBP_MINUS1, hwm->max_rows_per_field - h - vpad_b - 1));
VEC_WRITE(VEC_FRONT_PORCH,
- BITS(VEC_FRONT_PORCH_HFP_MINUS1, hpad - 1) |
- BITS(VEC_FRONT_PORCH_VFP_MINUS1, vpad - 1));
+ BITS(VEC_FRONT_PORCH_HFP_MINUS1, hpad_r - 1) |
+ BITS(VEC_FRONT_PORCH_VFP_MINUS1, vpad_b - 1));
VEC_WRITE(VEC_MODE,
- BITS(VEC_MODE_HIGH_WATER, 0xE0) |
- BITS(VEC_MODE_ALIGN16, !((w | mode->hdisplay) & 15)) |
- BITS(VEC_MODE_VFP_EN, (vpad > 0)) |
- BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h + vpad)) |
- BITS(VEC_MODE_HFP_EN, (hpad > 0)) |
- BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w + hpad)) |
- BITS(VEC_MODE_FIELDS_PER_FRAME_MINUS1, hwm->interlaced) |
+ BITS(VEC_MODE_HIGH_WATER, 0xE0) |
+ BITS(VEC_MODE_ALIGN16, !((w | mode->hdisplay) & 15)) |
+ 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));
- for (i = 0; i < ARRAY_SIZE(hwm->back_end_regs); ++i) {
- writel(hwm->back_end_regs[i],
- vec->hw_base[RP1VEC_HW_BLOCK_VEC] + 0x80 + 4 * i);
- }
- /* Apply modifications */
+ /* Configure the hardware "back end" (in the VDAC clock domain) */
+ VEC_WRITE(VEC_DAC_80,
+ BITS(VEC_DAC_80_U14_DE_BGN, hwm->de_bgn) |
+ BITS(VEC_DAC_80_U14_DE_END, hwm->de_bgn + wmax * rate));
+ rp1vec_write_regs(vec, 0x84, hwm->timing_regs, ARRAY_SIZE(hwm->timing_regs));
+ VEC_WRITE(VEC_DAC_C0, 0x0); /* DAC control/status -- not wired up in RP1 */
+ VEC_WRITE(VEC_DAC_C4, 0x007bffff); /* DAC control -- not wired up in RP1 */
+ misc = hwm->half_lines_per_field;
+ if (misc == 524 && (mode->vtotal >> vec->fake_31khz) == 263)
+ misc += 2;
if (tvstd == DRM_MODE_TV_MODE_NTSC_J && mode_family == 0) {
- /* Reduce pedestal (not quite to zero, for FIR overshoot); increase gain */
+ /* NTSC-J modification: reduce pedestal and increase gain */
VEC_WRITE(VEC_DAC_BC,
- BITS(VEC_DAC_BC_S11_PEDESTAL, 10) |
- (hwm->back_end_regs[(0xBC - 0x80) / 4] & ~VEC_DAC_BC_S11_PEDESTAL_BITS));
+ BITS(VEC_DAC_BC_U11_HALF_LINES_PER_FIELD, misc) |
+ BITS(VEC_DAC_BC_S11_PEDESTAL, 0x00a));
VEC_WRITE(VEC_DAC_C8,
BITS(VEC_DAC_C8_U16_SCALE_LUMA, 0x9400) |
- (hwm->back_end_regs[(0xC8 - 0x80) / 4] &
- ~VEC_DAC_C8_U16_SCALE_LUMA_BITS));
- } else if ((tvstd == DRM_MODE_TV_MODE_NTSC_443 || tvstd == DRM_MODE_TV_MODE_PAL) &&
- mode_family != 1) {
+ BITS(VEC_DAC_C8_U16_SCALE_SYNC, hwm->scale_sync));
+ } else {
+ VEC_WRITE(VEC_DAC_BC,
+ BITS(VEC_DAC_BC_U11_HALF_LINES_PER_FIELD, misc) |
+ BITS(VEC_DAC_BC_S11_PEDESTAL, hwm->pedestal));
+ VEC_WRITE(VEC_DAC_C8,
+ BITS(VEC_DAC_C8_U16_SCALE_LUMA, hwm->scale_luma) |
+ BITS(VEC_DAC_C8_U16_SCALE_SYNC, hwm->scale_sync));
+ }
+ VEC_WRITE(VEC_DAC_CC, (tvstd >= DRM_MODE_TV_MODE_SECAM) ? 0 : hwm->scale_burst_chroma);
+ VEC_WRITE(VEC_DAC_D0, 0x02000000); /* ADC offsets -- not needed in RP1? */
+ misc = hwm->misc;
+ if ((tvstd == DRM_MODE_TV_MODE_NTSC_443 || tvstd == DRM_MODE_TV_MODE_PAL) &&
+ mode_family != 1) {
/* Change colour carrier frequency to 4433618.75 Hz; disable hard sync */
VEC_WRITE(VEC_DAC_D4, 0xcc48c1d1);
VEC_WRITE(VEC_DAC_D8, 0x0a8262b2);
- VEC_WRITE(VEC_DAC_EC,
- hwm->back_end_regs[(0xEC - 0x80) / 4] & ~VEC_DAC_EC_SEQ_EN_BITS);
+ misc &= ~VEC_DAC_EC_SEQ_EN_BITS;
} else if (tvstd == DRM_MODE_TV_MODE_PAL_N && mode_family == 1) {
/* Change colour carrier frequency to 3582056.25 Hz */
VEC_WRITE(VEC_DAC_D4, 0x9ce075f7);
VEC_WRITE(VEC_DAC_D8, 0x087da511);
+ } else {
+ VEC_WRITE(VEC_DAC_D4, (u32)(hwm->nco_freq));
+ VEC_WRITE(VEC_DAC_D8, (u32)(hwm->nco_freq >> 32));
}
+ 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));
- /* Input pixel format conversion */
- for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
- if (my_formats[i].format == in_format)
- break;
- }
- if (i >= ARRAY_SIZE(my_formats)) {
- dev_err(&vec->pdev->dev, "%s: bad input format\n", __func__);
- i = 0;
- }
- VEC_WRITE(VEC_IMASK, my_formats[i].mask);
- VEC_WRITE(VEC_SHIFT, my_formats[i].shift);
- VEC_WRITE(VEC_RGBSZ, my_formats[i].rgbsz);
-
- VEC_WRITE(VEC_IRQ_FLAGS, 0xffffffff);
+ /* 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);
-
i = rp1vec_hw_busy(vec);
if (i)
dev_warn(&vec->pdev->dev,
@@ -464,6 +520,10 @@ void rp1vec_hw_update(struct rp1_vec *ve
*/
u64 a = addr + offset;
+ if (vec->fake_31khz) {
+ a += 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);