From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 25 Apr 2020 22:06:53 +0200 Subject: drm/meson: Add support for using a PHY for the CVBS DAC Currently the VPU driver hardcodes the initialization, power-on and power-off sequences for the CVBS DAC. The registers for the CVBS DAC are in the HHI register area. Also the CVBS DAC is a PHY so it can be modelled as such. Add support for using a PHY as CVBS DAC to de-couple the VPU driver from the HHI registers (at least for this part of the implementation). Register a platform device for the PHY (which creates a lookup entry to compensate for the missing .dtb entry) which takes over all HHI_VDAC_CNTL register management. Signed-off-by: Martin Blumenstingl --- drivers/gpu/drm/meson/Kconfig | 1 + drivers/gpu/drm/meson/meson_drv.h | 6 + drivers/gpu/drm/meson/meson_encoder_cvbs.c | 132 ++++++++-- drivers/gpu/drm/meson/meson_venc.c | 13 - 4 files changed, 110 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig index 111111111111..222222222222 100644 --- a/drivers/gpu/drm/meson/Kconfig +++ b/drivers/gpu/drm/meson/Kconfig @@ -12,6 +12,7 @@ config DRM_MESON select REGMAP_MMIO select MESON_CANVAS select CEC_CORE if CEC_NOTIFIER + imply PHY_MESON_CVBS_DAC config DRM_MESON_DW_HDMI tristate "HDMI Synopsys Controller support for Amlogic Meson Display" diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h index 111111111111..222222222222 100644 --- a/drivers/gpu/drm/meson/meson_drv.h +++ b/drivers/gpu/drm/meson/meson_drv.h @@ -16,6 +16,8 @@ struct drm_device; struct drm_plane; struct meson_drm; struct meson_afbcd_ops; +struct phy; +struct platform_device; enum vpu_compatible { VPU_COMPATIBLE_GXBB = 0, @@ -61,6 +63,10 @@ struct meson_drm { const struct meson_drm_soc_limits *limits; + struct phy *cvbs_dac; + bool cvbs_dac_enabled; + struct platform_device *cvbs_dac_pdev; + /* Components Data */ struct { bool osd1_enabled; diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c index 111111111111..222222222222 100644 --- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include @@ -24,12 +26,6 @@ #include "meson_vclk.h" #include "meson_encoder_cvbs.h" -/* HHI VDAC Registers */ -#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ -#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbd offset in data sheet */ -#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ -#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbe offset in data sheet */ - struct meson_encoder_cvbs { struct drm_encoder encoder; struct drm_bridge bridge; @@ -87,11 +83,28 @@ static int meson_encoder_cvbs_attach(struct drm_bridge *bridge, { struct meson_encoder_cvbs *meson_encoder_cvbs = bridge_to_meson_encoder_cvbs(bridge); + int ret; + + ret = phy_init(meson_encoder_cvbs->priv->cvbs_dac); + if (ret) + return ret; return drm_bridge_attach(bridge->encoder, meson_encoder_cvbs->next_bridge, &meson_encoder_cvbs->bridge, flags); } +static void meson_encoder_cvbs_detach(struct drm_bridge *bridge) +{ + struct meson_encoder_cvbs *meson_encoder_cvbs = + bridge_to_meson_encoder_cvbs(bridge); + int ret; + + ret = phy_exit(meson_encoder_cvbs->priv->cvbs_dac); + if (ret) + dev_err(meson_encoder_cvbs->priv->dev, + "Failed to exit the CVBS DAC\n"); +} + static int meson_encoder_cvbs_get_modes(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -148,6 +161,7 @@ static void meson_encoder_cvbs_atomic_enable(struct drm_bridge *bridge, struct drm_connector_state *conn_state; struct drm_crtc_state *crtc_state; struct drm_connector *connector; + int ret; connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); if (WARN_ON(!connector)) @@ -177,16 +191,13 @@ static void meson_encoder_cvbs_atomic_enable(struct drm_bridge *bridge, writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0, priv->io_base + _REG(VENC_VDAC_DACSEL0)); - if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) { - regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1); - regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0); - } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || - meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) { - regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001); - regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0); - } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { - regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001); - regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0); + if (!priv->cvbs_dac_enabled) { + ret = phy_power_on(priv->cvbs_dac); + if (ret) + dev_err(priv->dev, + "Failed to power on the CVBS DAC\n"); + else + priv->cvbs_dac_enabled = true; } } @@ -196,19 +207,22 @@ static void meson_encoder_cvbs_atomic_disable(struct drm_bridge *bridge, struct meson_encoder_cvbs *meson_encoder_cvbs = bridge_to_meson_encoder_cvbs(bridge); struct meson_drm *priv = meson_encoder_cvbs->priv; + int ret; - /* Disable CVBS VDAC */ - if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { - regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0); - regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0); - } else { - regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); - regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); - } + if (!priv->cvbs_dac_enabled) + return; + + ret = phy_power_off(priv->cvbs_dac); + if (ret) + dev_err(priv->dev, + "Failed to power off the CVBS DAC\n"); + else + priv->cvbs_dac_enabled = false; } static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = { .attach = meson_encoder_cvbs_attach, + .detach = meson_encoder_cvbs_detach, .mode_valid = meson_encoder_cvbs_mode_valid, .get_modes = meson_encoder_cvbs_get_modes, .atomic_enable = meson_encoder_cvbs_atomic_enable, @@ -219,6 +233,54 @@ static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset, }; +static int meson_cvbs_dac_probe(struct meson_drm *priv) +{ + struct platform_device *pdev; + const char *platform_id_name; + + priv->cvbs_dac = devm_phy_optional_get(priv->dev, "cvbs-dac"); + if (IS_ERR(priv->cvbs_dac)) + return dev_err_probe(priv->dev, PTR_ERR(priv->cvbs_dac), + "Failed to get the 'cvbs-dac' PHY\n"); + else if (priv->cvbs_dac) + return 0; + + switch (priv->compat) { + case VPU_COMPATIBLE_GXBB: + platform_id_name = "meson-gxbb-cvbs-dac"; + break; + case VPU_COMPATIBLE_GXL: + case VPU_COMPATIBLE_GXM: + platform_id_name = "meson-gxl-cvbs-dac"; + break; + case VPU_COMPATIBLE_G12A: + platform_id_name = "meson-g12a-cvbs-dac"; + break; + default: + return dev_err_probe(priv->dev, -EINVAL, + "No CVBS DAC platform ID found\n"); + } + + pdev = platform_device_register_data(priv->dev, platform_id_name, + PLATFORM_DEVID_AUTO, NULL, 0); + if (IS_ERR(pdev)) + return dev_err_probe(priv->dev, PTR_ERR(pdev), + "Failed to register fallback CVBS DAC PHY platform device\n"); + + priv->cvbs_dac = platform_get_drvdata(pdev); + if (IS_ERR(priv->cvbs_dac)) { + platform_device_unregister(pdev); + return dev_err_probe(priv->dev, PTR_ERR(priv->cvbs_dac), + "Failed to get the 'cvbs-dac' PHY from it's platform device\n"); + } + + dev_info(priv->dev, "Using fallback for old .dtbs without CVBS DAC\n"); + + priv->cvbs_dac_pdev = pdev; + + return 0; +} + int meson_encoder_cvbs_probe(struct meson_drm *priv) { struct drm_device *drm = priv->drm; @@ -255,6 +317,10 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv) meson_encoder_cvbs->priv = priv; + ret = meson_cvbs_dac_probe(priv); + if (ret) + return ret; + /* Encoder */ ret = drm_simple_encoder_init(priv->drm, &meson_encoder_cvbs->encoder, DRM_MODE_ENCODER_TVDAC); @@ -268,21 +334,27 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv) ret = drm_bridge_attach(&meson_encoder_cvbs->encoder, &meson_encoder_cvbs->bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (ret) { - dev_err(priv->dev, "Failed to attach bridge: %d\n", ret); - return ret; + dev_err_probe(priv->dev, ret, "Failed to attach bridge\n"); + goto err_unregister_cvbs_dac_pdev; } /* Initialize & attach Bridge Connector */ connector = drm_bridge_connector_init(priv->drm, &meson_encoder_cvbs->encoder); - if (IS_ERR(connector)) - return dev_err_probe(priv->dev, PTR_ERR(connector), - "Unable to create CVBS bridge connector\n"); + if (IS_ERR(connector)) { + ret = dev_err_probe(priv->dev, PTR_ERR(connector), + "Unable to create CVBS bridge connector\n"); + goto err_unregister_cvbs_dac_pdev; + } drm_connector_attach_encoder(connector, &meson_encoder_cvbs->encoder); priv->encoders[MESON_ENC_CVBS] = meson_encoder_cvbs; return 0; + +err_unregister_cvbs_dac_pdev: + platform_device_unregister(priv->cvbs_dac_pdev); + return ret; } void meson_encoder_cvbs_remove(struct meson_drm *priv) @@ -293,4 +365,6 @@ void meson_encoder_cvbs_remove(struct meson_drm *priv) meson_encoder_cvbs = priv->encoders[MESON_ENC_CVBS]; drm_bridge_remove(&meson_encoder_cvbs->bridge); } + + platform_device_unregister(priv->cvbs_dac_pdev); } diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c index 111111111111..222222222222 100644 --- a/drivers/gpu/drm/meson/meson_venc.c +++ b/drivers/gpu/drm/meson/meson_venc.c @@ -62,10 +62,6 @@ /* HHI Registers */ #define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */ -#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ -#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbb offset in data sheet */ -#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ -#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbc offset in data sheet */ #define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 offset in data sheet */ struct meson_cvbs_enci_mode meson_cvbs_enci_pal = { @@ -1968,15 +1964,6 @@ void meson_venc_disable_vsync(struct meson_drm *priv) void meson_venc_init(struct meson_drm *priv) { - /* Disable CVBS VDAC */ - if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) { - regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0); - regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 8); - } else { - regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); - regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8); - } - /* Power Down Dacs */ writel_relaxed(0xff, priv->io_base + _REG(VENC_VDAC_SETTING)); -- Armbian