Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3273fc63 authored by Neil Armstrong's avatar Neil Armstrong
Browse files

drm/meson: Make DMT timings parameters and pixel clock generic



Remove the modes timings tables for DMT modes and calculate the HW
paremeters from the modes timings.

Switch the DMT modes pixel clock calculation out of the static frequency
list to a generic calculation from a range of possible PLL dividers.

This patch is an intermediate step towards usage of the Common Clock
Framwework for PLL setup, by reworking the code to have common
sel_pll() function called by the CEA (HDMI) freq setup and the generic
DMT frequencies setup, we should be able to simply call clk_set_rate()
on the PLL clock handle in a near future.

The CEA (HDMI) and CVBS modes needs very specific clock paths that CCF will
never be able to determine by itself, so there is still some work to do for
a full handoff to CCF handling the clocks.

This setup permits setting non-CEA modes like :
- 1600x900-60Hz
- 1280x1024-75Hz
- 1280x1024-60Hz
- 1440x900-60Hz
- 1366x768-60Hz
- 1280x800-60Hz
- 1152x864-75Hz
- 1024x768-75Hz
- 1024x768-70Hz
- 1024x768-60Hz
- 832x624-75Hz
- 800x600-75Hz
- 800x600-72Hz
- 800x600-60Hz
- 640x480-75Hz
- 640x480-73Hz
- 640x480-67Hz

Signed-off-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Acked-by: default avatarJerome Brunet <jbrunet@baylibre.com>
[narmstrong: fixed trivial checkpatch issues]
Link: https://patchwork.freedesktop.org/patch/msgid/1531726814-14638-1-git-send-email-narmstrong@baylibre.com
parent 620eec75
Loading
Loading
Loading
Loading
+14 −8
Original line number Diff line number Diff line
@@ -329,6 +329,12 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,

	vclk_freq = mode->clock;

	if (!vic) {
		meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, vclk_freq,
				 vclk_freq, vclk_freq, false);
		return;
	}

	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
		vclk_freq *= 2;

@@ -542,10 +548,12 @@ static enum drm_mode_status
dw_hdmi_mode_valid(struct drm_connector *connector,
		   const struct drm_display_mode *mode)
{
	struct meson_drm *priv = connector->dev->dev_private;
	unsigned int vclk_freq;
	unsigned int venc_freq;
	unsigned int hdmi_freq;
	int vic = drm_match_cea_mode(mode);
	enum drm_mode_status status;

	DRM_DEBUG_DRIVER("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
		mode->base.id, mode->name, mode->vrefresh, mode->clock,
@@ -556,8 +564,11 @@ dw_hdmi_mode_valid(struct drm_connector *connector,

	/* Check against non-VIC supported modes */
	if (!vic) {
		if (!meson_venc_hdmi_supported_mode(mode))
			return MODE_BAD;
		status = meson_venc_hdmi_supported_mode(mode);
		if (status != MODE_OK)
			return status;

		return meson_vclk_dmt_supported_freq(priv, mode->clock);
	/* Check against supported VIC modes */
	} else if (!meson_venc_hdmi_supported_vic(vic))
		return MODE_BAD;
@@ -583,16 +594,11 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
	dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__,
		vclk_freq, venc_freq, hdmi_freq);

	/* Finally filter by configurable vclk frequencies */
	/* Finally filter by configurable vclk frequencies for VIC modes */
	switch (vclk_freq) {
	case 25175:
	case 40000:
	case 54000:
	case 65000:
	case 74250:
	case 108000:
	case 148500:
	case 162000:
	case 297000:
	case 594000:
		return MODE_OK;
+286 −370
Original line number Diff line number Diff line
@@ -320,32 +320,23 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
				CTS_VDAC_EN, CTS_VDAC_EN);
}


enum {
/* PLL	O1 O2 O3 VP DV     EN TX */
/* 4320 /4 /4 /1 /5 /1  => /2 /2 */
#define MESON_VCLK_HDMI_ENCI_54000	1
	MESON_VCLK_HDMI_ENCI_54000 = 1,
/* 4320 /4 /4 /1 /5 /1  => /1 /2 */
#define MESON_VCLK_HDMI_DDR_54000	2
	MESON_VCLK_HDMI_DDR_54000,
/* 2970 /4 /1 /1 /5 /1  => /1 /2 */
#define MESON_VCLK_HDMI_DDR_148500	3
/* 4028 /4 /4 /1 /5 /2  => /1 /1 */
#define MESON_VCLK_HDMI_25175		4
/* 3200 /4 /2 /1 /5 /2  => /1 /1 */
#define MESON_VCLK_HDMI_40000		5
/* 5200 /4 /2 /1 /5 /2  => /1 /1 */
#define MESON_VCLK_HDMI_65000		6
	MESON_VCLK_HDMI_DDR_148500,
/* 2970 /2 /2 /2 /5 /1  => /1 /1 */
#define MESON_VCLK_HDMI_74250		7
/* 4320 /4 /1 /1 /5 /2  => /1 /1 */
#define MESON_VCLK_HDMI_108000		8
	MESON_VCLK_HDMI_74250,
/* 2970 /1 /2 /2 /5 /1  => /1 /1 */
#define MESON_VCLK_HDMI_148500		9
/* 3240 /2 /1 /1 /5 /2  => /1 /1 */
#define MESON_VCLK_HDMI_162000		10
	MESON_VCLK_HDMI_148500,
/* 2970 /1 /1 /1 /5 /2  => /1 /1 */
#define MESON_VCLK_HDMI_297000		11
	MESON_VCLK_HDMI_297000,
/* 5940 /1 /1 /2 /5 /1  => /1 /1 */
#define MESON_VCLK_HDMI_594000		12
	MESON_VCLK_HDMI_594000
};

struct meson_vclk_params {
	unsigned int pll_base_freq;
@@ -411,46 +402,6 @@ struct meson_vclk_params {
		.vid_pll_div = VID_PLL_DIV_5,
		.vclk_div = 1,
	},
	[MESON_VCLK_HDMI_25175] = {
		.pll_base_freq = 4028000,
		.pll_od1 = 4,
		.pll_od2 = 4,
		.pll_od3 = 1,
		.vid_pll_div = VID_PLL_DIV_5,
		.vclk_div = 2,
	},
	[MESON_VCLK_HDMI_40000] = {
		.pll_base_freq = 3200000,
		.pll_od1 = 4,
		.pll_od2 = 2,
		.pll_od3 = 1,
		.vid_pll_div = VID_PLL_DIV_5,
		.vclk_div = 2,
	},
	[MESON_VCLK_HDMI_65000] = {
		.pll_base_freq = 5200000,
		.pll_od1 = 4,
		.pll_od2 = 2,
		.pll_od3 = 1,
		.vid_pll_div = VID_PLL_DIV_5,
		.vclk_div = 2,
	},
	[MESON_VCLK_HDMI_108000] = {
		.pll_base_freq = 4320000,
		.pll_od1 = 4,
		.pll_od2 = 1,
		.pll_od3 = 1,
		.vid_pll_div = VID_PLL_DIV_5,
		.vclk_div = 2,
	},
	[MESON_VCLK_HDMI_162000] = {
		.pll_base_freq = 3240000,
		.pll_od1 = 2,
		.pll_od2 = 1,
		.pll_od3 = 1,
		.vid_pll_div = VID_PLL_DIV_5,
		.vclk_div = 2,
	},
};

static inline unsigned int pll_od_to_reg(unsigned int od)
@@ -470,19 +421,20 @@ static inline unsigned int pll_od_to_reg(unsigned int od)
	return 0;
}

void meson_hdmi_pll_set(struct meson_drm *priv,
			unsigned int base,
			unsigned int od1,
			unsigned int od2,
			unsigned int od3)
void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
			       unsigned int frac, unsigned int od1,
			       unsigned int od2, unsigned int od3)
{
	unsigned int val;

	if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
		switch (base) {
		case 2970000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000200 | m);
		if (frac)
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2,
				     0x00004000 | frac);
		else
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2,
				     0x00000000);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
@@ -495,224 +447,14 @@ void meson_hdmi_pll_set(struct meson_drm *priv,
		/* Poll for lock bit */
		regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
					 val, (val & HDMI_PLL_LOCK), 10, 0);

			/* div_frac */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
						0xFFFF,  0x4e00);
			break;

		case 3200000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000242);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);

			/* unreset */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
						BIT(28), 0);

			/* Poll for lock bit */
			regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
					val, (val & HDMI_PLL_LOCK), 10, 0);

			/* div_frac */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
						0xFFFF,  0x4aab);
			break;

		case 3240000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000243);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);

			/* unreset */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
						BIT(28), 0);

			/* Poll for lock bit */
			regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
					val, (val & HDMI_PLL_LOCK), 10, 0);

			/* div_frac */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
						0xFFFF,  0x4800);
			break;

		case 3865000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000250);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);

			/* unreset */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
						BIT(28), 0);

			/* Poll for lock bit */
			regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
					val, (val & HDMI_PLL_LOCK), 10, 0);

			/* div_frac */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
						0xFFFF,  0x4855);
			break;

		case 4028000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x58000253);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);

			/* unreset */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
						BIT(28), 0);

			/* Poll for lock bit */
			regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
					val, (val & HDMI_PLL_LOCK), 10, 0);

			/* div_frac */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
						0xFFFF,  0x4eab);
			break;

		case 4320000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800025a);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);

			/* unreset */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
						BIT(28), 0);

			/* Poll for lock bit */
			regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
					val, (val & HDMI_PLL_LOCK), 10, 0);
			break;

		case 5940000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800027b);
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
						0xFFFF,  0x4c00);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x135c5091);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);

			/* unreset */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
						BIT(28), 0);

			/* Poll for lock bit */
			regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
					val, (val & HDMI_PLL_LOCK), 10, 0);
			break;

		case 5200000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800026c);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x135c5091);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);

			/* unreset */
			regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
						BIT(28), 0);

			/* Poll for lock bit */
			regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL,
					val, (val & HDMI_PLL_LOCK), 10, 0);
			break;
		};
	} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
		   meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
		switch (base) {
		case 2970000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
			break;

		case 3200000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000285);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb155);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
			break;

		case 3240000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000287);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
			break;

		case 3865000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002a1);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb02b);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
			break;

		case 4028000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002a7);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb355);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
			break;

		case 4320000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002b4);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
			break;

		case 5940000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002f7);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb200);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x40000200 | m);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000 | frac);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
		regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
			break;

		case 5200000:
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002d8);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb2ab);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
			regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
			break;

		};

		/* Reset PLL */
		regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
@@ -723,7 +465,7 @@ void meson_hdmi_pll_set(struct meson_drm *priv,
		/* Poll for lock bit */
		regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
				(val & HDMI_PLL_LOCK), 10, 0);
	};
	}

	if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
		regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
@@ -748,80 +490,148 @@ void meson_hdmi_pll_set(struct meson_drm *priv,
			meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
		regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
				3 << 19, pll_od_to_reg(od3) << 19);

}

void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
		      unsigned int vclk_freq, unsigned int venc_freq,
		      unsigned int dac_freq, bool hdmi_use_enci)
#define XTAL_FREQ 24000

static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv,
					 unsigned int pll_freq)
{
	unsigned int freq;
	unsigned int hdmi_tx_div;
	unsigned int venc_div;
	/* The GXBB PLL has a /2 pre-multiplier */
	if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
		pll_freq /= 2;

	if (target == MESON_VCLK_TARGET_CVBS) {
		meson_venci_cvbs_clock_config(priv);
		return;
	return pll_freq / XTAL_FREQ;
}

	hdmi_tx_div = vclk_freq / dac_freq;
#define HDMI_FRAC_MAX_GXBB	4096
#define HDMI_FRAC_MAX_GXL	1024

	if (hdmi_tx_div == 0) {
		pr_err("Fatal Error, invalid HDMI-TX freq %d\n",
				dac_freq);
		return;
static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
					    unsigned int m,
					    unsigned int pll_freq)
{
	unsigned int parent_freq = XTAL_FREQ;
	unsigned int frac_max = HDMI_FRAC_MAX_GXL;
	unsigned int frac_m;
	unsigned int frac;

	/* The GXBB PLL has a /2 pre-multiplier and a larger FRAC width */
	if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
		frac_max = HDMI_FRAC_MAX_GXBB;
		parent_freq *= 2;
	}

	venc_div = vclk_freq / venc_freq;
	/* We can have a perfect match !*/
	if (pll_freq / m == parent_freq &&
	    pll_freq % m == 0)
		return 0;

	if (venc_div == 0) {
		pr_err("Fatal Error, invalid HDMI venc freq %d\n",
				venc_freq);
		return;
	frac = div_u64((u64)pll_freq * (u64)frac_max, parent_freq);
	frac_m = m * frac_max;
	if (frac_m > frac)
		return frac_max;
	frac -= frac_m;

	return min((u16)frac, (u16)(frac_max - 1));
}

	switch (vclk_freq) {
	case 54000:
		if (hdmi_use_enci)
			freq = MESON_VCLK_HDMI_ENCI_54000;
		else
			freq = MESON_VCLK_HDMI_DDR_54000;
		break;
	case 25175:
		freq = MESON_VCLK_HDMI_25175;
		break;
	case 40000:
		freq = MESON_VCLK_HDMI_40000;
		break;
	case 65000:
		freq = MESON_VCLK_HDMI_65000;
		break;
	case 74250:
		freq = MESON_VCLK_HDMI_74250;
		break;
	case 108000:
		freq = MESON_VCLK_HDMI_108000;
		break;
	case 148500:
		if (dac_freq != 148500)
			freq = MESON_VCLK_HDMI_DDR_148500;
		else
			freq = MESON_VCLK_HDMI_148500;
		break;
	case 162000:
		freq = MESON_VCLK_HDMI_162000;
		break;
	case 297000:
		freq = MESON_VCLK_HDMI_297000;
		break;
	case 594000:
		freq = MESON_VCLK_HDMI_594000;
		break;
	default:
		pr_err("Fatal Error, invalid HDMI vclk freq %d\n",
			vclk_freq);
static bool meson_hdmi_pll_validate_params(struct meson_drm *priv,
					   unsigned int m,
					   unsigned int frac)
{
	if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
		/* Empiric supported min/max dividers */
		if (m < 53 || m > 123)
			return false;
		if (frac >= HDMI_FRAC_MAX_GXBB)
			return false;
	} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
		   meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
		/* Empiric supported min/max dividers */
		if (m < 106 || m > 247)
			return false;
		if (frac >= HDMI_FRAC_MAX_GXL)
			return false;
	}

	return true;
}

static bool meson_hdmi_pll_find_params(struct meson_drm *priv,
				       unsigned int freq,
				       unsigned int *m,
				       unsigned int *frac,
				       unsigned int *od)
{
	/* Cycle from /16 to /2 */
	for (*od = 16 ; *od > 1 ; *od >>= 1) {
		*m = meson_hdmi_pll_get_m(priv, freq * *od);
		if (!*m)
			continue;
		*frac = meson_hdmi_pll_get_frac(priv, *m, freq * *od);

		DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d\n",
				 freq, *m, *frac, *od);

		if (meson_hdmi_pll_validate_params(priv, *m, *frac))
			return true;
	}

	return false;
}

/* pll_freq is the frequency after the OD dividers */
enum drm_mode_status
meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq)
{
	unsigned int od, m, frac;

	/* In DMT mode, path after PLL is always /10 */
	freq *= 10;

	if (meson_hdmi_pll_find_params(priv, freq, &m, &frac, &od))
		return MODE_OK;

	return MODE_CLOCK_RANGE;
}
EXPORT_SYMBOL_GPL(meson_vclk_dmt_supported_freq);

/* pll_freq is the frequency after the OD dividers */
static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
				       unsigned int pll_freq)
{
	unsigned int od, m, frac, od1, od2, od3;

	if (meson_hdmi_pll_find_params(priv, pll_freq, &m, &frac, &od)) {
		od3 = 1;
		if (od < 4) {
			od1 = 2;
			od2 = 1;
		} else {
			od2 = od / 4;
			od1 = od / od2;
		}

		DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d/%d/%d\n",
				 pll_freq, m, frac, od1, od2, od3);

		meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);

		return;
	}

	DRM_ERROR("Fatal, unable to find parameters for PLL freq %d\n",
		  pll_freq);
}

static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
			   unsigned int od1, unsigned int od2, unsigned int od3,
			   unsigned int vid_pll_div, unsigned int vclk_div,
			   unsigned int hdmi_tx_div, unsigned int venc_div,
			   bool hdmi_use_enci)
{
	/* Set HDMI-TX sys clock */
	regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL,
			   CTS_HDMI_SYS_SEL_MASK, 0);
@@ -831,19 +641,49 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
			   CTS_HDMI_SYS_EN, CTS_HDMI_SYS_EN);

	/* Set HDMI PLL rate */
	meson_hdmi_pll_set(priv, params[freq].pll_base_freq,
			   params[freq].pll_od1,
			   params[freq].pll_od2,
			   params[freq].pll_od3);
	if (!od1 && !od2 && !od3) {
		meson_hdmi_pll_generic_set(priv, pll_base_freq);
	} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
		switch (pll_base_freq) {
		case 2970000:
			meson_hdmi_pll_set_params(priv, 0x3d, 0xe00,
						  od1, od2, od3);
			break;
		case 4320000:
			meson_hdmi_pll_set_params(priv, 0x5a, 0,
						  od1, od2, od3);
			break;
		case 5940000:
			meson_hdmi_pll_set_params(priv, 0x7b, 0xc00,
						  od1, od2, od3);
			break;
		}
	} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
		   meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
		switch (pll_base_freq) {
		case 2970000:
			meson_hdmi_pll_set_params(priv, 0x7b, 0x300,
						  od1, od2, od3);
			break;
		case 4320000:
			meson_hdmi_pll_set_params(priv, 0xb4, 0,
						  od1, od2, od3);
			break;
		case 5940000:
			meson_hdmi_pll_set_params(priv, 0xf7, 0x200,
						  od1, od2, od3);
			break;
		}
	}

	/* Setup vid_pll divider */
	meson_vid_pll_set(priv, params[freq].vid_pll_div);
	meson_vid_pll_set(priv, vid_pll_div);

	/* Set VCLK div */
	regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL,
			   VCLK_SEL_MASK, 0);
	regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV,
			   VCLK_DIV_MASK, params[freq].vclk_div - 1);
			   VCLK_DIV_MASK, vclk_div - 1);

	/* Set HDMI-TX source */
	switch (hdmi_tx_div) {
@@ -981,4 +821,80 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,

	regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_EN, VCLK_EN);
}

void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
		      unsigned int vclk_freq, unsigned int venc_freq,
		      unsigned int dac_freq, bool hdmi_use_enci)
{
	unsigned int freq;
	unsigned int hdmi_tx_div;
	unsigned int venc_div;

	if (target == MESON_VCLK_TARGET_CVBS) {
		meson_venci_cvbs_clock_config(priv);
		return;
	} else if (target == MESON_VCLK_TARGET_DMT) {
		/* The DMT clock path is fixed after the PLL:
		 * - automatic PLL freq + OD management
		 * - vid_pll_div = VID_PLL_DIV_5
		 * - vclk_div = 2
		 * - hdmi_tx_div = 1
		 * - venc_div = 1
		 * - encp encoder
		 */
		meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0,
			       VID_PLL_DIV_5, 2, 1, 1, false);
		return;
	}

	hdmi_tx_div = vclk_freq / dac_freq;

	if (hdmi_tx_div == 0) {
		pr_err("Fatal Error, invalid HDMI-TX freq %d\n",
		       dac_freq);
		return;
	}

	venc_div = vclk_freq / venc_freq;

	if (venc_div == 0) {
		pr_err("Fatal Error, invalid HDMI venc freq %d\n",
		       venc_freq);
		return;
	}

	switch (vclk_freq) {
	case 54000:
		if (hdmi_use_enci)
			freq = MESON_VCLK_HDMI_ENCI_54000;
		else
			freq = MESON_VCLK_HDMI_DDR_54000;
		break;
	case 74250:
		freq = MESON_VCLK_HDMI_74250;
		break;
	case 148500:
		if (dac_freq != 148500)
			freq = MESON_VCLK_HDMI_DDR_148500;
		else
			freq = MESON_VCLK_HDMI_148500;
		break;
	case 297000:
		freq = MESON_VCLK_HDMI_297000;
		break;
	case 594000:
		freq = MESON_VCLK_HDMI_594000;
		break;
	default:
		pr_err("Fatal Error, invalid HDMI vclk freq %d\n",
		       vclk_freq);
		return;
	}

	meson_vclk_set(priv, params[freq].pll_base_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);
}
EXPORT_SYMBOL_GPL(meson_vclk_setup);
+4 −0
Original line number Diff line number Diff line
@@ -24,11 +24,15 @@
enum {
	MESON_VCLK_TARGET_CVBS = 0,
	MESON_VCLK_TARGET_HDMI = 1,
	MESON_VCLK_TARGET_DMT = 2,
};

/* 27MHz is the CVBS Pixel Clock */
#define MESON_VCLK_CVBS			27000

enum drm_mode_status
meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq);

void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
		      unsigned int vclk_freq, unsigned int venc_freq,
		      unsigned int dac_freq, bool hdmi_use_enci);