diff options
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_crtc.c')
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_crtc.c | 217 |
1 files changed, 135 insertions, 82 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 0108613e79d5..bef9d45ef1df 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -50,8 +50,17 @@ #define HVS_FIFO_LATENCY_PIX 6 -#define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) -#define CRTC_READ(offset) readl(vc4_crtc->regs + (offset)) +#define CRTC_WRITE(offset, val) \ + do { \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + writel(val, vc4_crtc->regs + (offset)); \ + } while (0) + +#define CRTC_READ(offset) \ + ({ \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + readl(vc4_crtc->regs + (offset)); \ + }) static const struct debugfs_reg32 crtc_regs[] = { VC4_REG32(PV_CONTROL), @@ -326,8 +335,14 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 || vc4_encoder->type == VC4_ENCODER_TYPE_DSI1); bool is_dsi1 = vc4_encoder->type == VC4_ENCODER_TYPE_DSI1; + bool is_vec = vc4_encoder->type == VC4_ENCODER_TYPE_VEC; u32 format = is_dsi1 ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24; u8 ppc = pv_data->pixels_per_clock; + + u16 vert_bp = mode->crtc_vtotal - mode->crtc_vsync_end; + u16 vert_sync = mode->crtc_vsync_end - mode->crtc_vsync_start; + u16 vert_fp = mode->crtc_vsync_start - mode->crtc_vdisplay; + bool debug_dump_regs = false; int idx; @@ -355,49 +370,60 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode VC4_SET_FIELD(mode->hdisplay * pixel_rep / ppc, PV_HORZB_HACTIVE)); - CRTC_WRITE(PV_VERTA, - VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end + - interlace, - PV_VERTA_VBP) | - VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start, - PV_VERTA_VSYNC)); - CRTC_WRITE(PV_VERTB, - VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay, - PV_VERTB_VFP) | - VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE)); - if (interlace) { + bool odd_field_first = false; + u32 field_delay = mode->htotal * pixel_rep / (2 * ppc); + u16 vert_bp_even = vert_bp; + u16 vert_fp_even = vert_fp; + + if (is_vec) { + /* VEC (composite output) */ + ++field_delay; + if (mode->htotal == 858) { + /* 525-line mode (NTSC or PAL-M) */ + odd_field_first = true; + } + } + + if (odd_field_first) + ++vert_fp_even; + else + ++vert_bp; + CRTC_WRITE(PV_VERTA_EVEN, - VC4_SET_FIELD(mode->crtc_vtotal - - mode->crtc_vsync_end, - PV_VERTA_VBP) | - VC4_SET_FIELD(mode->crtc_vsync_end - - mode->crtc_vsync_start, - PV_VERTA_VSYNC)); + VC4_SET_FIELD(vert_bp_even, PV_VERTA_VBP) | + VC4_SET_FIELD(vert_sync, PV_VERTA_VSYNC)); CRTC_WRITE(PV_VERTB_EVEN, - VC4_SET_FIELD(mode->crtc_vsync_start - - mode->crtc_vdisplay, - PV_VERTB_VFP) | + VC4_SET_FIELD(vert_fp_even, PV_VERTB_VFP) | VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE)); - /* We set up first field even mode for HDMI. VEC's - * NTSC mode would want first field odd instead, once - * we support it (to do so, set ODD_FIRST and put the - * delay in VSYNCD_EVEN instead). + /* We set up first field even mode for HDMI and VEC's PAL. + * For NTSC, we need first field odd. */ CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS | (is_dsi ? PV_VCONTROL_DSI : 0) | PV_VCONTROL_INTERLACE | - VC4_SET_FIELD(mode->htotal * pixel_rep / (2 * ppc), - PV_VCONTROL_ODD_DELAY)); - CRTC_WRITE(PV_VSYNCD_EVEN, 0); + (odd_field_first + ? PV_VCONTROL_ODD_FIRST + : VC4_SET_FIELD(field_delay, + PV_VCONTROL_ODD_DELAY))); + CRTC_WRITE(PV_VSYNCD_EVEN, + (odd_field_first ? field_delay : 0)); } else { CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS | (is_dsi ? PV_VCONTROL_DSI : 0)); + CRTC_WRITE(PV_VSYNCD_EVEN, 0); } + CRTC_WRITE(PV_VERTA, + VC4_SET_FIELD(vert_bp, PV_VERTA_VBP) | + VC4_SET_FIELD(vert_sync, PV_VERTA_VSYNC)); + CRTC_WRITE(PV_VERTB, + VC4_SET_FIELD(vert_fp, PV_VERTB_VFP) | + VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE)); + if (is_dsi) CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep); @@ -486,21 +512,6 @@ static int vc4_crtc_disable(struct drm_crtc *crtc, return 0; } -static struct drm_encoder *vc4_crtc_get_encoder_by_type(struct drm_crtc *crtc, - enum vc4_encoder_type type) -{ - struct drm_encoder *encoder; - - drm_for_each_encoder(encoder, crtc->dev) { - struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); - - if (vc4_encoder->type == type) - return encoder; - } - - return NULL; -} - int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) { struct drm_device *drm = crtc->dev; @@ -536,7 +547,7 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc); encoder_type = pv_data->encoder_types[encoder_sel]; - encoder = vc4_crtc_get_encoder_by_type(crtc, encoder_type); + encoder = vc4_find_encoder_by_type(drm, encoder_type); if (WARN_ON(!encoder)) return 0; @@ -690,8 +701,8 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state, } } -static int vc4_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_atomic_state *state) +int vc4_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) { struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); @@ -711,7 +722,7 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); if (vc4_encoder->type == VC4_ENCODER_TYPE_HDMI0) { - vc4_state->hvs_load = max(mode->clock * mode->hdisplay / mode->htotal + 1000, + vc4_state->hvs_load = max(mode->clock * mode->hdisplay / mode->htotal + 8000, mode->clock * 9 / 10) * 1000; } else { vc4_state->hvs_load = mode->clock * 1000; @@ -1096,12 +1107,9 @@ int vc4_crtc_late_register(struct drm_crtc *crtc) struct drm_device *drm = crtc->dev; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc); - int ret; - ret = vc4_debugfs_add_regset32(drm->primary, crtc_data->debugfs_name, - &vc4_crtc->regset); - if (ret) - return ret; + vc4_debugfs_add_regset32(drm, crtc_data->debugfs_name, + &vc4_crtc->regset); return 0; } @@ -1131,8 +1139,9 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { .get_scanout_position = vc4_crtc_get_scanout_position, }; -static const struct vc4_pv_data bcm2835_pv0_data = { +const struct vc4_pv_data bcm2835_pv0_data = { .base = { + .name = "pixelvalve-0", .debugfs_name = "crtc0_regs", .hvs_available_channels = BIT(0), .hvs_output = 0, @@ -1145,8 +1154,9 @@ static const struct vc4_pv_data bcm2835_pv0_data = { }, }; -static const struct vc4_pv_data bcm2835_pv1_data = { +const struct vc4_pv_data bcm2835_pv1_data = { .base = { + .name = "pixelvalve-1", .debugfs_name = "crtc1_regs", .hvs_available_channels = BIT(2), .hvs_output = 2, @@ -1159,8 +1169,9 @@ static const struct vc4_pv_data bcm2835_pv1_data = { }, }; -static const struct vc4_pv_data bcm2835_pv2_data = { +const struct vc4_pv_data bcm2835_pv2_data = { .base = { + .name = "pixelvalve-2", .debugfs_name = "crtc2_regs", .hvs_available_channels = BIT(1), .hvs_output = 1, @@ -1173,8 +1184,9 @@ static const struct vc4_pv_data bcm2835_pv2_data = { }, }; -static const struct vc4_pv_data bcm2711_pv0_data = { +const struct vc4_pv_data bcm2711_pv0_data = { .base = { + .name = "pixelvalve-0", .debugfs_name = "crtc0_regs", .hvs_available_channels = BIT(0), .hvs_output = 0, @@ -1187,8 +1199,9 @@ static const struct vc4_pv_data bcm2711_pv0_data = { }, }; -static const struct vc4_pv_data bcm2711_pv1_data = { +const struct vc4_pv_data bcm2711_pv1_data = { .base = { + .name = "pixelvalve-1", .debugfs_name = "crtc1_regs", .hvs_available_channels = BIT(0) | BIT(1) | BIT(2), .hvs_output = 3, @@ -1201,8 +1214,9 @@ static const struct vc4_pv_data bcm2711_pv1_data = { }, }; -static const struct vc4_pv_data bcm2711_pv2_data = { +const struct vc4_pv_data bcm2711_pv2_data = { .base = { + .name = "pixelvalve-2", .debugfs_name = "crtc2_regs", .hvs_available_channels = BIT(0) | BIT(1) | BIT(2), .hvs_output = 4, @@ -1214,8 +1228,9 @@ static const struct vc4_pv_data bcm2711_pv2_data = { }, }; -static const struct vc4_pv_data bcm2711_pv3_data = { +const struct vc4_pv_data bcm2711_pv3_data = { .base = { + .name = "pixelvalve-3", .debugfs_name = "crtc3_regs", .hvs_available_channels = BIT(1), .hvs_output = 1, @@ -1227,8 +1242,9 @@ static const struct vc4_pv_data bcm2711_pv3_data = { }, }; -static const struct vc4_pv_data bcm2711_pv4_data = { +const struct vc4_pv_data bcm2711_pv4_data = { .base = { + .name = "pixelvalve-4", .debugfs_name = "crtc4_regs", .hvs_available_channels = BIT(0) | BIT(1) | BIT(2), .hvs_output = 5, @@ -1278,31 +1294,44 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, } } -int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, - const struct drm_crtc_funcs *crtc_funcs, - const struct drm_crtc_helper_funcs *crtc_helper_funcs) +/** + * __vc4_crtc_init - Initializes a CRTC + * @drm: DRM Device + * @pdev: CRTC Platform Device + * @vc4_crtc: CRTC Object to Initialize + * @data: Configuration data associated with this CRTC + * @primary_plane: Primary plane for CRTC + * @crtc_funcs: Callbacks for the new CRTC + * @crtc_helper_funcs: Helper Callbacks for the new CRTC + * @feeds_txp: Is this CRTC connected to the TXP? + * + * Initializes our private CRTC structure. This function is mostly + * relevant for KUnit testing, all other users should use + * vc4_crtc_init() instead. + * + * Returns: + * 0 on success, a negative error code on failure. + */ +int __vc4_crtc_init(struct drm_device *drm, + struct platform_device *pdev, + struct vc4_crtc *vc4_crtc, + const struct vc4_crtc_data *data, + struct drm_plane *primary_plane, + const struct drm_crtc_funcs *crtc_funcs, + const struct drm_crtc_helper_funcs *crtc_helper_funcs, + bool feeds_txp) { struct vc4_dev *vc4 = to_vc4_dev(drm); struct drm_crtc *crtc = &vc4_crtc->base; - struct drm_plane *primary_plane; unsigned int i; int ret; - /* For now, we create just the primary and the legacy cursor - * planes. We should be able to stack more planes on easily, - * but to do that we would need to compute the bandwidth - * requirement of the plane configuration, and reject ones - * that will take too much. - */ - primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0); - if (IS_ERR(primary_plane)) { - dev_err(drm->dev, "failed to construct primary plane\n"); - return PTR_ERR(primary_plane); - } - + vc4_crtc->data = data; + vc4_crtc->pdev = pdev; + vc4_crtc->feeds_txp = feeds_txp; spin_lock_init(&vc4_crtc->irq_lock); ret = drmm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, - crtc_funcs, NULL); + crtc_funcs, data->name); if (ret) return ret; @@ -1328,6 +1357,31 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, return 0; } +int vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev, + struct vc4_crtc *vc4_crtc, + const struct vc4_crtc_data *data, + const struct drm_crtc_funcs *crtc_funcs, + const struct drm_crtc_helper_funcs *crtc_helper_funcs, + bool feeds_txp) +{ + struct drm_plane *primary_plane; + + /* For now, we create just the primary and the legacy cursor + * planes. We should be able to stack more planes on easily, + * but to do that we would need to compute the bandwidth + * requirement of the plane configuration, and reject ones + * that will take too much. + */ + primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0); + if (IS_ERR(primary_plane)) { + dev_err(drm->dev, "failed to construct primary plane\n"); + return PTR_ERR(primary_plane); + } + + return __vc4_crtc_init(drm, pdev, vc4_crtc, data, primary_plane, + crtc_funcs, crtc_helper_funcs, feeds_txp); +} + static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -1345,8 +1399,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) pv_data = of_device_get_match_data(dev); if (!pv_data) return -ENODEV; - vc4_crtc->data = &pv_data->base; - vc4_crtc->pdev = pdev; vc4_crtc->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(vc4_crtc->regs)) @@ -1356,8 +1408,9 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) vc4_crtc->regset.regs = crtc_regs; vc4_crtc->regset.nregs = ARRAY_SIZE(crtc_regs); - ret = vc4_crtc_init(drm, vc4_crtc, - &vc4_crtc_funcs, &vc4_crtc_helper_funcs); + ret = vc4_crtc_init(drm, pdev, vc4_crtc, &pv_data->base, + &vc4_crtc_funcs, &vc4_crtc_helper_funcs, + false); if (ret) return ret; vc4_set_crtc_possible_masks(drm, crtc); |