diff options
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_vec.c')
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_vec.c | 273 |
1 files changed, 112 insertions, 161 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 11fc3d6f66b1..0b3333865702 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -14,6 +14,7 @@ */ #include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> #include <drm/drm_edid.h> #include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> @@ -160,48 +161,28 @@ struct vc4_vec_variant { /* General VEC hardware state. */ struct vc4_vec { + struct vc4_encoder encoder; + struct drm_connector connector; + struct platform_device *pdev; const struct vc4_vec_variant *variant; - struct drm_encoder *encoder; - struct drm_connector *connector; - void __iomem *regs; struct clk *clock; - const struct vc4_vec_tv_mode *tv_mode; - struct debugfs_regset32 regset; }; #define VEC_READ(offset) readl(vec->regs + (offset)) #define VEC_WRITE(offset, val) writel(val, vec->regs + (offset)) -/* VC4 VEC encoder KMS struct */ -struct vc4_vec_encoder { - struct vc4_encoder base; - struct vc4_vec *vec; -}; - -static inline struct vc4_vec_encoder * -to_vc4_vec_encoder(struct drm_encoder *encoder) +static inline struct vc4_vec * +encoder_to_vc4_vec(struct drm_encoder *encoder) { - return container_of(encoder, struct vc4_vec_encoder, base.base); + return container_of(encoder, struct vc4_vec, encoder.base); } -/* VC4 VEC connector KMS struct */ -struct vc4_vec_connector { - struct drm_connector base; - struct vc4_vec *vec; - - /* Since the connector is attached to just the one encoder, - * this is the reference to it so we can do the best_encoder() - * hook. - */ - struct drm_encoder *encoder; -}; - enum vc4_vec_tv_mode_id { VC4_VEC_TV_MODE_NTSC, VC4_VEC_TV_MODE_NTSC_J, @@ -211,7 +192,9 @@ enum vc4_vec_tv_mode_id { struct vc4_vec_tv_mode { const struct drm_display_mode *mode; - void (*mode_set)(struct vc4_vec *vec); + u32 config0; + u32 config1; + u32 custom_freq; }; static const struct debugfs_reg32 vec_regs[] = { @@ -241,63 +224,41 @@ static const struct debugfs_reg32 vec_regs[] = { VC4_REG32(VEC_DAC_MISC), }; -static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec) -{ - VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN); - VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); -} - -static void vc4_vec_ntsc_j_mode_set(struct vc4_vec *vec) -{ - VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD); - VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); -} - static const struct drm_display_mode ntsc_mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500, 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, - 480, 480 + 3, 480 + 3 + 3, 480 + 3 + 3 + 16, 0, + 480, 480 + 7, 480 + 7 + 6, 525, 0, DRM_MODE_FLAG_INTERLACE) }; -static void vc4_vec_pal_mode_set(struct vc4_vec *vec) -{ - VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD); - VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); -} - -static void vc4_vec_pal_m_mode_set(struct vc4_vec *vec) -{ - VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD); - VEC_WRITE(VEC_CONFIG1, - VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ); - VEC_WRITE(VEC_FREQ3_2, 0x223b); - VEC_WRITE(VEC_FREQ1_0, 0x61d1); -} - static const struct drm_display_mode pal_mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500, 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, - 576, 576 + 2, 576 + 2 + 3, 576 + 2 + 3 + 20, 0, + 576, 576 + 4, 576 + 4 + 6, 625, 0, DRM_MODE_FLAG_INTERLACE) }; static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { [VC4_VEC_TV_MODE_NTSC] = { .mode = &ntsc_mode, - .mode_set = vc4_vec_ntsc_mode_set, + .config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN, + .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_NTSC_J] = { .mode = &ntsc_mode, - .mode_set = vc4_vec_ntsc_j_mode_set, + .config0 = VEC_CONFIG0_NTSC_STD, + .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_PAL] = { .mode = &pal_mode, - .mode_set = vc4_vec_pal_mode_set, + .config0 = VEC_CONFIG0_PAL_BDGHI_STD, + .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_PAL_M] = { .mode = &pal_mode, - .mode_set = vc4_vec_pal_m_mode_set, + .config0 = VEC_CONFIG0_PAL_BDGHI_STD, + .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ, + .custom_freq = 0x223b61d1, }, }; @@ -307,12 +268,6 @@ vc4_vec_connector_detect(struct drm_connector *connector, bool force) return connector_status_unknown; } -static void vc4_vec_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static int vc4_vec_connector_get_modes(struct drm_connector *connector) { struct drm_connector_state *state = connector->state; @@ -333,7 +288,6 @@ static int vc4_vec_connector_get_modes(struct drm_connector *connector) static const struct drm_connector_funcs vc4_vec_connector_funcs = { .detect = vc4_vec_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = vc4_vec_connector_destroy, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -343,42 +297,38 @@ static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = .get_modes = vc4_vec_connector_get_modes, }; -static struct drm_connector *vc4_vec_connector_init(struct drm_device *dev, - struct vc4_vec *vec) +static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec) { - struct drm_connector *connector = NULL; - struct vc4_vec_connector *vec_connector; - - vec_connector = devm_kzalloc(dev->dev, sizeof(*vec_connector), - GFP_KERNEL); - if (!vec_connector) - return ERR_PTR(-ENOMEM); + struct drm_connector *connector = &vec->connector; + int ret; - connector = &vec_connector->base; connector->interlace_allowed = true; - vec_connector->encoder = vec->encoder; - vec_connector->vec = vec; + ret = drmm_connector_init(dev, connector, &vc4_vec_connector_funcs, + DRM_MODE_CONNECTOR_Composite, NULL); + if (ret) + return ret; - drm_connector_init(dev, connector, &vc4_vec_connector_funcs, - DRM_MODE_CONNECTOR_Composite); drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs); drm_object_attach_property(&connector->base, dev->mode_config.tv_mode_property, VC4_VEC_TV_MODE_NTSC); - vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC]; - drm_connector_attach_encoder(connector, vec->encoder); + drm_connector_attach_encoder(connector, &vec->encoder.base); - return connector; + return 0; } -static void vc4_vec_encoder_disable(struct drm_encoder *encoder) +static void vc4_vec_encoder_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { - struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); - struct vc4_vec *vec = vc4_vec_encoder->vec; - int ret; + struct drm_device *drm = encoder->dev; + struct vc4_vec *vec = encoder_to_vc4_vec(encoder); + int idx, ret; + + if (!drm_dev_enter(drm, &idx)) + return; VEC_WRITE(VEC_CFG, 0); VEC_WRITE(VEC_DAC_MISC, @@ -392,20 +342,35 @@ static void vc4_vec_encoder_disable(struct drm_encoder *encoder) ret = pm_runtime_put(&vec->pdev->dev); if (ret < 0) { DRM_ERROR("Failed to release power domain: %d\n", ret); - return; + goto err_dev_exit; } + + drm_dev_exit(idx); + return; + +err_dev_exit: + drm_dev_exit(idx); } -static void vc4_vec_encoder_enable(struct drm_encoder *encoder) +static void vc4_vec_encoder_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { - struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); - struct vc4_vec *vec = vc4_vec_encoder->vec; - int ret; + struct drm_device *drm = encoder->dev; + struct vc4_vec *vec = encoder_to_vc4_vec(encoder); + struct drm_connector *connector = &vec->connector; + struct drm_connector_state *conn_state = + drm_atomic_get_new_connector_state(state, connector); + const struct vc4_vec_tv_mode *tv_mode = + &vc4_vec_tv_modes[conn_state->tv.mode]; + int idx, ret; + + if (!drm_dev_enter(drm, &idx)) + return; ret = pm_runtime_get_sync(&vec->pdev->dev); if (ret < 0) { DRM_ERROR("Failed to retain power domain: %d\n", ret); - return; + goto err_dev_exit; } /* @@ -418,13 +383,13 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder) ret = clk_set_rate(vec->clock, 108000000); if (ret) { DRM_ERROR("Failed to set clock rate: %d\n", ret); - return; + goto err_put_runtime_pm; } ret = clk_prepare_enable(vec->clock); if (ret) { DRM_ERROR("Failed to turn on core clock: %d\n", ret); - return; + goto err_put_runtime_pm; } /* Reset the different blocks */ @@ -455,29 +420,27 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder) /* Mask all interrupts. */ VEC_WRITE(VEC_MASK0, 0); - vec->tv_mode->mode_set(vec); + VEC_WRITE(VEC_CONFIG0, tv_mode->config0); + VEC_WRITE(VEC_CONFIG1, tv_mode->config1); + + if (tv_mode->custom_freq) { + VEC_WRITE(VEC_FREQ3_2, + (tv_mode->custom_freq >> 16) & 0xffff); + VEC_WRITE(VEC_FREQ1_0, + tv_mode->custom_freq & 0xffff); + } VEC_WRITE(VEC_DAC_MISC, VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N); VEC_WRITE(VEC_CFG, VEC_CFG_VEC_EN); -} + drm_dev_exit(idx); + return; -static bool vc4_vec_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static void vc4_vec_encoder_atomic_mode_set(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); - struct vc4_vec *vec = vc4_vec_encoder->vec; - - vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode]; +err_put_runtime_pm: + pm_runtime_put(&vec->pdev->dev); +err_dev_exit: + drm_dev_exit(idx); } static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder, @@ -496,11 +459,27 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder, } static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = { - .disable = vc4_vec_encoder_disable, - .enable = vc4_vec_encoder_enable, - .mode_fixup = vc4_vec_encoder_mode_fixup, .atomic_check = vc4_vec_encoder_atomic_check, - .atomic_mode_set = vc4_vec_encoder_atomic_mode_set, + .atomic_disable = vc4_vec_encoder_disable, + .atomic_enable = vc4_vec_encoder_enable, +}; + +static int vc4_vec_late_register(struct drm_encoder *encoder) +{ + struct drm_device *drm = encoder->dev; + struct vc4_vec *vec = encoder_to_vc4_vec(encoder); + int ret; + + ret = vc4_debugfs_add_regset32(drm->primary, "vec_regs", + &vec->regset); + if (ret) + return ret; + + return 0; +} + +static const struct drm_encoder_funcs vc4_vec_encoder_funcs = { + .late_register = vc4_vec_late_register, }; static const struct vc4_vec_variant bcm2835_vec_variant = { @@ -532,9 +511,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_vec *vec; - struct vc4_vec_encoder *vc4_vec_encoder; int ret; ret = drm_mode_create_tv_properties(drm, ARRAY_SIZE(tv_mode_names), @@ -542,18 +519,11 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; - vec = devm_kzalloc(dev, sizeof(*vec), GFP_KERNEL); + vec = drmm_kzalloc(drm, sizeof(*vec), GFP_KERNEL); if (!vec) return -ENOMEM; - vc4_vec_encoder = devm_kzalloc(dev, sizeof(*vc4_vec_encoder), - GFP_KERNEL); - if (!vc4_vec_encoder) - return -ENOMEM; - vc4_vec_encoder->base.type = VC4_ENCODER_TYPE_VEC; - vc4_vec_encoder->vec = vec; - vec->encoder = &vc4_vec_encoder->base.base; - + vec->encoder.type = VC4_ENCODER_TYPE_VEC; vec->pdev = pdev; vec->variant = (const struct vc4_vec_variant *) of_device_get_match_data(dev); @@ -572,49 +542,30 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) return ret; } - pm_runtime_enable(dev); - - drm_simple_encoder_init(drm, vec->encoder, DRM_MODE_ENCODER_TVDAC); - drm_encoder_helper_add(vec->encoder, &vc4_vec_encoder_helper_funcs); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; - vec->connector = vc4_vec_connector_init(drm, vec); - if (IS_ERR(vec->connector)) { - ret = PTR_ERR(vec->connector); - goto err_destroy_encoder; - } + ret = drmm_encoder_init(drm, &vec->encoder.base, + &vc4_vec_encoder_funcs, + DRM_MODE_ENCODER_TVDAC, + NULL); + if (ret) + return ret; - dev_set_drvdata(dev, vec); + drm_encoder_helper_add(&vec->encoder.base, &vc4_vec_encoder_helper_funcs); - vc4->vec = vec; + ret = vc4_vec_connector_init(drm, vec); + if (ret) + return ret; - vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset); + dev_set_drvdata(dev, vec); return 0; - -err_destroy_encoder: - drm_encoder_cleanup(vec->encoder); - pm_runtime_disable(dev); - - return ret; -} - -static void vc4_vec_unbind(struct device *dev, struct device *master, - void *data) -{ - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); - struct vc4_vec *vec = dev_get_drvdata(dev); - - vc4_vec_connector_destroy(vec->connector); - drm_encoder_cleanup(vec->encoder); - pm_runtime_disable(dev); - - vc4->vec = NULL; } static const struct component_ops vc4_vec_ops = { .bind = vc4_vec_bind, - .unbind = vc4_vec_unbind, }; static int vc4_vec_dev_probe(struct platform_device *pdev) |