armbian-build/patch/kernel/archive/meson-6.10/0057-drm-meson-Meson8-Meson...

486 lines
14 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Sat, 25 Apr 2020 22:13:51 +0200
Subject: drm/meson: Meson8/Meson8b/Meson8m2 VCLK - HACK
WiP
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
drivers/gpu/drm/meson/meson_drv.c | 101 ++++++-
drivers/gpu/drm/meson/meson_drv.h | 32 ++
drivers/gpu/drm/meson/meson_vclk.c | 146 ++++++++++
drivers/gpu/drm/meson/meson_venc.c | 24 +-
4 files changed, 293 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c
index 111111111111..222222222222 100644
--- a/drivers/gpu/drm/meson/meson_drv.c
+++ b/drivers/gpu/drm/meson/meson_drv.c
@@ -170,6 +170,35 @@ static void meson_vpu_init(struct meson_drm *priv)
}
}
+static int meson_video_clock_init(struct meson_drm *priv)
+{
+ int ret;
+
+ ret = clk_bulk_prepare(VPU_VID_CLK_NUM, priv->vid_clks);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "Failed to prepare the video clocks\n");
+
+ ret = clk_bulk_prepare(priv->num_intr_clks, priv->intr_clks);
+ if (ret)
+ return dev_err_probe(priv->dev, ret,
+ "Failed to prepare the interrupt clocks\n");
+
+ return 0;
+}
+
+static void meson_video_clock_exit(struct meson_drm *priv)
+{
+ if (priv->clk_dac_enabled)
+ clk_disable(priv->clk_dac);
+
+ if (priv->clk_venc_enabled)
+ clk_disable(priv->clk_venc);
+
+ clk_bulk_unprepare(priv->num_intr_clks, priv->intr_clks);
+ clk_bulk_unprepare(VPU_VID_CLK_NUM, priv->vid_clks);
+}
+
static void meson_fbdev_setup(struct meson_drm *priv)
{
unsigned int preferred_bpp;
@@ -263,10 +292,59 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
priv->compat = match->compat;
priv->afbcd.ops = match->afbcd_ops;
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) {
+ priv->vid_pll_resets[VPU_RESET_VID_PLL_PRE].id = "vid_pll_pre";
+ priv->vid_pll_resets[VPU_RESET_VID_PLL_POST].id = "vid_pll_post";
+ priv->vid_pll_resets[VPU_RESET_VID_PLL_SOFT_PRE].id = "vid_pll_soft_pre";
+ priv->vid_pll_resets[VPU_RESET_VID_PLL_SOFT_POST].id = "vid_pll_soft_post";
+
+ ret = devm_reset_control_bulk_get_exclusive(dev,
+ VPU_RESET_VID_PLL_NUM,
+ priv->vid_pll_resets);
+ if (ret)
+ goto free_drm;
+
+ priv->intr_clks[0].id = "vpu_intr";
+ priv->intr_clks[1].id = "hdmi_intr_sync";
+ priv->intr_clks[2].id = "venci_int";
+ priv->num_intr_clks = 3;
+
+ ret = devm_clk_bulk_get(dev, priv->num_intr_clks,
+ priv->intr_clks);
+ if (ret)
+ goto free_drm;
+
+ priv->vid_clks[VPU_VID_CLK_TMDS].id = "tmds";
+ priv->vid_clks[VPU_VID_CLK_HDMI_TX_PIXEL].id = "hdmi_tx_pixel";
+ priv->vid_clks[VPU_VID_CLK_CTS_ENCP].id = "cts_encp";
+ priv->vid_clks[VPU_VID_CLK_CTS_ENCI].id = "cts_enci";
+ priv->vid_clks[VPU_VID_CLK_CTS_ENCT].id = "cts_enct";
+ priv->vid_clks[VPU_VID_CLK_CTS_ENCL].id = "cts_encl";
+ priv->vid_clks[VPU_VID_CLK_CTS_VDAC0].id = "cts_vdac0";
+
+ ret = devm_clk_bulk_get(dev, VPU_VID_CLK_NUM, priv->vid_clks);
+ if (ret)
+ goto free_drm;
+ } else {
+ priv->intr_clks[0].id = "vpu_intr";
+ priv->num_intr_clks = 1;
+
+ ret = devm_clk_bulk_get_optional(dev, priv->num_intr_clks,
+ priv->intr_clks);
+ if (ret)
+ goto free_drm;
+ }
+
+ ret = meson_video_clock_init(priv);
+ if (ret)
+ goto free_drm;
+
regs = devm_platform_ioremap_resource_byname(pdev, "vpu");
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
- goto free_drm;
+ goto video_clock_exit;
}
priv->io_base = regs;
@@ -281,7 +359,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
regs = devm_ioremap(dev, res->start, resource_size(res));
if (!regs) {
ret = -EADDRNOTAVAIL;
- goto free_drm;
+ goto video_clock_exit;
}
priv->hhi = devm_regmap_init_mmio(dev, regs,
@@ -290,36 +368,36 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
dev_err(&pdev->dev,
"Couldn't create the HHI regmap\n");
ret = PTR_ERR(priv->hhi);
- goto free_drm;
+ goto video_clock_exit;
}
}
priv->canvas = meson_canvas_get(dev);
if (IS_ERR(priv->canvas)) {
ret = PTR_ERR(priv->canvas);
- goto free_drm;
+ goto video_clock_exit;
}
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
if (ret)
- goto free_drm;
+ goto video_clock_exit;
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
if (ret) {
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
- goto free_drm;
+ goto video_clock_exit;
}
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
if (ret) {
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
- goto free_drm;
+ goto video_clock_exit;
}
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
if (ret) {
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
- goto free_drm;
+ goto video_clock_exit;
}
priv->vsync_irq = platform_get_irq(pdev, 0);
@@ -425,6 +503,8 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
exit_afbcd:
if (priv->afbcd.ops)
priv->afbcd.ops->exit(priv);
+video_clock_exit:
+ meson_video_clock_exit(priv);
free_drm:
drm_dev_put(drm);
@@ -469,6 +549,8 @@ static void meson_drv_unbind(struct device *dev)
if (priv->afbcd.ops)
priv->afbcd.ops->exit(priv);
+
+ meson_video_clock_exit(priv);
}
static const struct component_master_ops meson_drv_master_ops = {
@@ -483,6 +565,8 @@ static int __maybe_unused meson_drv_pm_suspend(struct device *dev)
if (!priv)
return 0;
+ // TODO: video clock suspend
+
return drm_mode_config_helper_suspend(priv->drm);
}
@@ -493,6 +577,7 @@ static int __maybe_unused meson_drv_pm_resume(struct device *dev)
if (!priv)
return 0;
+ meson_video_clock_init(priv);
meson_vpu_init(priv);
meson_venc_init(priv);
meson_vpp_init(priv);
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
@@ -7,9 +7,11 @@
#ifndef __MESON_DRV_H
#define __MESON_DRV_H
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
struct drm_crtc;
struct drm_device;
@@ -45,6 +47,25 @@ struct meson_drm_soc_limits {
unsigned int max_hdmi_phy_freq;
};
+enum vpu_bulk_clk_id {
+ VPU_VID_CLK_TMDS = 0,
+ VPU_VID_CLK_HDMI_TX_PIXEL,
+ VPU_VID_CLK_CTS_ENCP,
+ VPU_VID_CLK_CTS_ENCI,
+ VPU_VID_CLK_CTS_ENCT,
+ VPU_VID_CLK_CTS_ENCL,
+ VPU_VID_CLK_CTS_VDAC0,
+ VPU_VID_CLK_NUM
+};
+
+enum vpu_bulk_vid_pll_reset_id {
+ VPU_RESET_VID_PLL_PRE = 0,
+ VPU_RESET_VID_PLL_POST,
+ VPU_RESET_VID_PLL_SOFT_PRE,
+ VPU_RESET_VID_PLL_SOFT_POST,
+ VPU_RESET_VID_PLL_NUM
+};
+
struct meson_drm {
struct device *dev;
enum vpu_compatible compat;
@@ -70,6 +91,17 @@ struct meson_drm {
bool cvbs_dac_enabled;
struct platform_device *cvbs_dac_pdev;
+ struct clk_bulk_data intr_clks[3];
+ unsigned int num_intr_clks;
+ bool intr_clks_enabled;
+ struct clk_bulk_data vid_clks[VPU_VID_CLK_NUM];
+ bool vid_clk_rate_exclusive[VPU_VID_CLK_NUM];
+ struct clk *clk_venc;
+ bool clk_venc_enabled;
+ struct clk *clk_dac;
+ bool clk_dac_enabled;
+ struct reset_control_bulk_data vid_pll_resets[VPU_RESET_VID_PLL_NUM];
+
/* Components Data */
struct {
bool osd1_enabled;
diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c
index 111111111111..222222222222 100644
--- a/drivers/gpu/drm/meson/meson_vclk.c
+++ b/drivers/gpu/drm/meson/meson_vclk.c
@@ -732,6 +732,11 @@ meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq)
return MODE_CLOCK_HIGH;
}
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2))
+ return MODE_OK;
+
if (meson_hdmi_pll_find_params(priv, freq, &m, &frac, &od))
return MODE_OK;
@@ -784,6 +789,11 @@ meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
return MODE_CLOCK_HIGH;
}
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2))
+ return MODE_OK;
+
for (i = 0 ; params[i].pixel_freq ; ++i) {
DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n",
i, params[i].pixel_freq,
@@ -1024,6 +1034,128 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_EN, VCLK_EN);
}
+static int meson_vclk_set_rate_exclusive(struct meson_drm *priv,
+ enum vpu_bulk_clk_id clk_id,
+ unsigned int rate_khz)
+{
+ struct clk *clk = priv->vid_clks[clk_id].clk;
+ int ret;
+
+ ret = clk_set_rate_exclusive(clk, rate_khz * 1000UL);
+ if (ret)
+ return ret;
+
+ priv->vid_clk_rate_exclusive[clk_id] = true;
+
+ return 0;
+}
+
+static void meson_vclk_disable_ccf(struct meson_drm *priv)
+{
+ unsigned int i;
+
+ /* allow all clocks to be changed in _enable again */
+ for (i = 0; i < VPU_VID_CLK_NUM; i++) {
+ if (!priv->vid_clk_rate_exclusive[i])
+ continue;
+
+ clk_rate_exclusive_put(priv->vid_clks[i].clk);
+ priv->vid_clk_rate_exclusive[i] = false;
+ }
+
+ if (priv->clk_dac_enabled) {
+ clk_disable(priv->clk_dac);
+ priv->clk_dac_enabled = false;
+ }
+
+ if (priv->clk_venc_enabled) {
+ clk_disable(priv->clk_venc);
+ priv->clk_venc_enabled = false;
+ }
+}
+
+static int meson_vclk_enable_ccf(struct meson_drm *priv, unsigned int target,
+ bool hdmi_use_enci, unsigned int phy_freq,
+ unsigned int dac_freq, unsigned int venc_freq)
+{
+ enum vpu_bulk_clk_id venc_clk_id, dac_clk_id;
+ int ret;
+
+ if (target == MESON_VCLK_TARGET_CVBS || hdmi_use_enci)
+ venc_clk_id = VPU_VID_CLK_CTS_ENCI;
+ else
+ venc_clk_id = VPU_VID_CLK_CTS_ENCP;
+
+ if (target == MESON_VCLK_TARGET_CVBS)
+ dac_clk_id = VPU_VID_CLK_CTS_VDAC0;
+ else
+ dac_clk_id = VPU_VID_CLK_HDMI_TX_PIXEL;
+
+ /*
+ * The TMDS clock also updates the PLL. Protect the PLL rate so all
+ * following clocks are derived from the PLL setting which matches the
+ * TMDS clock.
+ */
+ ret = meson_vclk_set_rate_exclusive(priv, VPU_VID_CLK_TMDS, phy_freq);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set TMDS clock to %ukHz: %d\n",
+ phy_freq, ret);
+ goto out_enable_clocks;
+ }
+
+ /*
+ * The DAC clock may be derived from a parent of the VENC clock so we
+ * must protect the VENC clock from changing it's rate. This works
+ * because the DAC freq can be divided by the VENC clock.
+ */
+ ret = meson_vclk_set_rate_exclusive(priv, venc_clk_id, venc_freq);
+ if (ret) {
+ dev_warn(priv->dev,
+ "Failed to set VENC clock to %ukHz while TMDS clock is %ukHz: %d\n",
+ venc_freq, phy_freq, ret);
+ goto out_enable_clocks;
+ }
+
+ priv->clk_venc = priv->vid_clks[venc_clk_id].clk;
+
+ /*
+ * after changing any of the VID_PLL_* clocks (which can happen when
+ * update the VENC clock rate) we need to assert and then de-assert the
+ * VID_DIVIDER_CNTL_* reset lines.
+ */
+ reset_control_bulk_assert(VPU_RESET_VID_PLL_NUM, priv->vid_pll_resets);
+ reset_control_bulk_deassert(VPU_RESET_VID_PLL_NUM, priv->vid_pll_resets);
+
+ ret = meson_vclk_set_rate_exclusive(priv, dac_clk_id, dac_freq);
+ if (ret) {
+ dev_warn(priv->dev,
+ "Failed to set pixel clock to %ukHz while TMDS clock is %ukHz: %d\n",
+ dac_freq, phy_freq, ret);
+ goto out_enable_clocks;
+ }
+
+ priv->clk_dac = priv->vid_clks[dac_clk_id].clk;
+
+out_enable_clocks:
+ ret = clk_enable(priv->clk_venc);
+ if (ret)
+ dev_err(priv->dev,
+ "Failed to re-enable the VENC clock at %ukHz: %d\n",
+ venc_freq, ret);
+ else
+ priv->clk_venc_enabled = true;
+
+ ret = clk_enable(priv->clk_dac);
+ if (ret)
+ dev_err(priv->dev,
+ "Failed to re-enable the pixel clock at %ukHz: %d\n",
+ dac_freq, ret);
+ else
+ priv->clk_dac_enabled = true;
+
+ return ret;
+}
+
void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
unsigned int phy_freq, unsigned int vclk_freq,
unsigned int venc_freq, unsigned int dac_freq,
@@ -1034,6 +1166,20 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
unsigned int hdmi_tx_div;
unsigned int venc_div;
+ if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8B) ||
+ meson_vpu_is_compatible(priv, VPU_COMPATIBLE_M8M2)) {
+ /* CVBS video clocks are generated off a 1296MHz base clock */
+ if (target == MESON_VCLK_TARGET_CVBS)
+ phy_freq = 1296000;
+
+ dev_err(priv->dev, "%s(target: %u, phy: %u, dac: %u, venc: %u, hdmi_use_enci: %u)\n", __func__, target, phy_freq, dac_freq, venc_freq, hdmi_use_enci);
+ meson_vclk_disable_ccf(priv);
+ meson_vclk_enable_ccf(priv, target, hdmi_use_enci, phy_freq,
+ dac_freq, venc_freq);
+ return;
+ }
+
if (target == MESON_VCLK_TARGET_CVBS) {
meson_venci_cvbs_clock_config(priv);
return;
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
@@ -1954,14 +1954,34 @@ void meson_venc_enable_vsync(struct meson_drm *priv)
priv->io_base + _REG(VENC_INTCTRL));
}
- if (priv->hhi)
+ if (priv->intr_clks[0].clk) {
+ if (!priv->intr_clks_enabled) {
+ int ret;
+
+ ret = clk_bulk_enable(priv->num_intr_clks,
+ priv->intr_clks);
+ if (ret)
+ dev_err(priv->dev,
+ "Failed to enable the interrupt clocks\n");
+ else
+ priv->intr_clks_enabled = true;
+ }
+ } else {
regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), BIT(25));
+ }
}
void meson_venc_disable_vsync(struct meson_drm *priv)
{
- if (priv->hhi)
+ if (priv->intr_clks[0].clk) {
+ if (priv->intr_clks_enabled) {
+ clk_bulk_disable(priv->num_intr_clks,
+ priv->intr_clks);
+ priv->intr_clks_enabled = false;
+ }
+ } else {
regmap_update_bits(priv->hhi, HHI_GCLK_MPEG2, BIT(25), 0);
+ }
writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL));
}
--
Armbian