Commit b9f20bfa authored by Martin Blumenstingl's avatar Martin Blumenstingl
Browse files

drm/meson: Meson8/Meson8b/Meson8m2 VCLK - HACK

parent 76819907
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -304,6 +304,53 @@ 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[0] = devm_reset_control_get_exclusive(dev, "vid_pll_pre");
		if (IS_ERR(priv->vid_pll_resets[0]))
			return PTR_ERR(priv->vid_pll_resets[0]);

		priv->vid_pll_resets[1] = devm_reset_control_get_exclusive(dev, "vid_pll_post");
		if (IS_ERR(priv->vid_pll_resets[1]))
			return PTR_ERR(priv->vid_pll_resets[1]);

		priv->vid_pll_resets[2] = devm_reset_control_get_exclusive(dev, "vid_pll_soft_pre");
		if (IS_ERR(priv->vid_pll_resets[2]))
			return PTR_ERR(priv->vid_pll_resets[2]);

		priv->vid_pll_resets[3] = devm_reset_control_get_exclusive(dev, "vid_pll_soft_post");
		if (IS_ERR(priv->vid_pll_resets[3]))
			return PTR_ERR(priv->vid_pll_resets[3]);

		priv->clk_bulk[VPU_BULK_CLK_HDMI_PLL].id = "hdmi_pll_dco";
		priv->clk_bulk[VPU_BULK_CLK_HDMI_PLL_LVDS_OUT].id = "hdmi_pll_lvds_od";
		priv->clk_bulk[VPU_BULK_CLK_HDMI_PLL_HDMI_OUT].id = "hdmi_pll_hdmi_od";
		priv->clk_bulk[VPU_BULK_CLK_PLL_VID].id = "vid_pll";
		priv->clk_bulk[VPU_BULK_CLK_VID_PLL_FINAL_DIV].id = "vid_pll_final_div";
		priv->clk_bulk[VPU_BULK_CLK_HDMI_TX_PIXEL].id = "hdmi_tx_pixel";
		priv->clk_bulk[VPU_BULK_CLK_CTS_ENCP].id = "cts_encp";
		priv->clk_bulk[VPU_BULK_CLK_CTS_ENCI].id = "cts_enci";
		priv->clk_bulk[VPU_BULK_CLK_CTS_ENCT].id = "cts_enct";
		priv->clk_bulk[VPU_BULK_CLK_CTS_ENCL].id = "cts_encl";
		priv->clk_bulk[VPU_BULK_CLK_CTS_VDAC0].id = "cts_vdac0";

		// TODO: error handling below
		for (i = 0; i < ARRAY_SIZE(priv->vid_pll_resets); i++) {
			ret = reset_control_deassert(priv->vid_pll_resets[i]);
			if (ret)
				return ret;
		}

		ret = devm_clk_bulk_get(dev, VPU_BULK_CLK_NUM, priv->clk_bulk);
		if (ret)
			return ret;

		ret = clk_bulk_prepare_enable(VPU_BULK_CLK_NUM, priv->clk_bulk);
		if (ret)
			return ret;
	}

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
	regs = devm_ioremap_resource(dev, res);
	if (IS_ERR(regs)) {
@@ -490,6 +537,8 @@ static void meson_drv_unbind(struct device *dev)
		priv->afbcd.ops->reset(priv);
		meson_rdma_free(priv);
	}

	clk_bulk_disable_unprepare(VPU_BULK_CLK_NUM, priv->clk_bulk);
}

static const struct component_master_ops meson_drv_master_ops = {
+22 −0
Original line number Diff line number Diff line
@@ -7,10 +7,12 @@
#ifndef __MESON_DRV_H
#define __MESON_DRV_H

#include <linux/clk.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>

struct drm_crtc;
struct drm_device;
@@ -37,6 +39,21 @@ struct meson_drm_soc_limits {
	unsigned int max_hdmi_phy_freq;
};

enum vpu_bulk_clk_id {
	VPU_BULK_CLK_HDMI_PLL = 0,
	VPU_BULK_CLK_HDMI_PLL_LVDS_OUT,
	VPU_BULK_CLK_HDMI_PLL_HDMI_OUT,
	VPU_BULK_CLK_PLL_VID,
	VPU_BULK_CLK_VID_PLL_FINAL_DIV,
	VPU_BULK_CLK_HDMI_TX_PIXEL,
	VPU_BULK_CLK_CTS_ENCP,
	VPU_BULK_CLK_CTS_ENCI,
	VPU_BULK_CLK_CTS_ENCT,
	VPU_BULK_CLK_CTS_ENCL,
	VPU_BULK_CLK_CTS_VDAC0,
	VPU_BULK_CLK_NUM
};

struct meson_drm {
	struct device *dev;
	enum vpu_compatible compat;
@@ -57,6 +74,11 @@ struct meson_drm {

	const struct meson_drm_soc_limits *limits;

	struct clk_bulk_data clk_bulk[VPU_BULK_CLK_NUM];
	struct clk *clk_venc;
	struct clk *clk_dac;
	struct reset_control *vid_pll_resets[4];

	/* Components Data */
	struct {
		bool osd1_enabled;
+80 −5
Original line number Diff line number Diff line
@@ -1024,6 +1024,58 @@ 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);
}

// TODO: error handling
static void meson_vclk_setup_ccf(struct meson_drm *priv,
				 unsigned int target, bool hdmi_use_enci,
				 unsigned int pll_freq, unsigned int phy_freq,
				 unsigned int dac_freq, unsigned int venc_freq)
{
	unsigned int i;

	dev_err(priv->dev, "%s(target: %u, pll: %u, phy: %u, dac: %u, venc: %u, hdmi_use_enci: %u)\n", __func__, target, pll_freq, phy_freq, dac_freq, venc_freq, hdmi_use_enci);

	// HACK: we don't support 2.971GHz on the HDMI PLL yet
	if (target == MESON_VCLK_TARGET_HDMI && pll_freq >= 2970000)
		pll_freq /= 2;

	clk_disable(priv->clk_dac);
	clk_disable(priv->clk_venc);
	clk_rate_exclusive_put(priv->clk_venc);

	clk_set_rate(priv->clk_bulk[VPU_BULK_CLK_HDMI_PLL].clk,
			pll_freq * 1000UL);
	clk_set_rate(priv->clk_bulk[VPU_BULK_CLK_HDMI_PLL_HDMI_OUT].clk,
			phy_freq * 1000UL);

	if (target == MESON_VCLK_TARGET_CVBS || hdmi_use_enci)
		priv->clk_venc = priv->clk_bulk[VPU_BULK_CLK_CTS_ENCI].clk;
	else
		priv->clk_venc = priv->clk_bulk[VPU_BULK_CLK_CTS_ENCP].clk;

	if (target == MESON_VCLK_TARGET_CVBS)
		priv->clk_dac = priv->clk_bulk[VPU_BULK_CLK_CTS_VDAC0].clk;
	else
		priv->clk_dac = priv->clk_bulk[VPU_BULK_CLK_HDMI_TX_PIXEL].clk;

	/*
	 * 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.
	 */
	clk_set_rate_exclusive(priv->clk_venc, venc_freq * 1000UL);
	clk_set_rate(priv->clk_dac, dac_freq * 1000UL);

	for (i = 0; i < ARRAY_SIZE(priv->vid_pll_resets); i++)
		reset_control_assert(priv->vid_pll_resets[i]);
	for (i = 0; i < ARRAY_SIZE(priv->vid_pll_resets); i++)
		reset_control_deassert(priv->vid_pll_resets[i]);

	if (clk_enable(priv->clk_venc))
		dev_err(priv->dev, "Failed to re-enable the VENC clock\n");
	if (clk_enable(priv->clk_dac))
		dev_err(priv->dev, "Failed to re-enable the DAC clock\n");
}

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 +1086,17 @@ 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)) &&
	    (target == MESON_VCLK_TARGET_CVBS ||
	     target == MESON_VCLK_TARGET_DMT)) {
		meson_vclk_setup_ccf(priv,
				     target, hdmi_use_enci,
				     phy_freq, phy_freq, dac_freq, venc_freq);
		return;
	}

	if (target == MESON_VCLK_TARGET_CVBS) {
		meson_venci_cvbs_clock_config(priv);
		return;
@@ -1102,10 +1165,22 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
		return;
	}

	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)) {
		meson_vclk_setup_ccf(priv,
				     target,
				     hdmi_use_enci,
				     params[freq].pll_freq,
				     params[freq].phy_freq,
				     params[freq].pixel_freq,
				     venc_freq);
	} else {
		meson_vclk_set(priv, params[freq].pll_freq,
			       params[freq].pll_od1, params[freq].pll_od2,
			       params[freq].pll_od3, params[freq].vid_pll_div,
			       params[freq].vclk_div, hdmi_tx_div, venc_div,
			       hdmi_use_enci, vic_alternate_clock);
	}
}
EXPORT_SYMBOL_GPL(meson_vclk_setup);