318 lines
11 KiB
Diff
318 lines
11 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|
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 <martin.blumenstingl@googlemail.com>
|
|
---
|
|
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 <linux/export.h>
|
|
#include <linux/of_graph.h>
|
|
+#include <linux/phy/phy.h>
|
|
+#include <linux/platform_device.h>
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_bridge.h>
|
|
@@ -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
|
|
|