From 4e81411f2a7fa068a49c34c87b692ba99801041b Mon Sep 17 00:00:00 2001 From: Nick Hollinghurst 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 --- 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: + * ",,,,,,,,[,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,,,,,,,,[,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);