Commit ff217bc7 authored by Neil Armstrong's avatar Neil Armstrong

drm/meson: Add support for VIC alternate timings

This change is an attempt to handle the alternate clock for the CEA mode.
60Hz vs. 59.94Hz, 30Hz vs 29.97Hz or 24Hz vs 23.97Hz on the Amlogic Meson SoC
DRM Driver pixel clock generation.

The actual clock generation will be moved to the Common Clock framework once
all the video clock are handled by the Amlogic Meson SoC clock driver,
then these alternate timings will be handled in the same time in a cleaner
fashion.
Signed-off-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Reviewed-by: default avatarMaxime Jourdan <mjourdan@baylibre.com>
[narmstrong: fix maybe-uninitialized warnings after applying]
Link: https://patchwork.freedesktop.org/patch/msgid/1541501675-3928-1-git-send-email-narmstrong@baylibre.com
parent 5d0bfe44
...@@ -594,17 +594,7 @@ dw_hdmi_mode_valid(struct drm_connector *connector, ...@@ -594,17 +594,7 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__, dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__,
vclk_freq, venc_freq, hdmi_freq); vclk_freq, venc_freq, hdmi_freq);
/* Finally filter by configurable vclk frequencies for VIC modes */ return meson_vclk_vic_supported_freq(vclk_freq);
switch (vclk_freq) {
case 54000:
case 74250:
case 148500:
case 297000:
case 594000:
return MODE_OK;
}
return MODE_CLOCK_RANGE;
} }
/* Encoder */ /* Encoder */
......
...@@ -117,6 +117,8 @@ ...@@ -117,6 +117,8 @@
#define HDMI_PLL_RESET BIT(28) #define HDMI_PLL_RESET BIT(28)
#define HDMI_PLL_LOCK BIT(31) #define HDMI_PLL_LOCK BIT(31)
#define FREQ_1000_1001(_freq) DIV_ROUND_CLOSEST(_freq * 1000, 1001)
/* VID PLL Dividers */ /* VID PLL Dividers */
enum { enum {
VID_PLL_DIV_1 = 0, VID_PLL_DIV_1 = 0,
...@@ -323,7 +325,7 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv) ...@@ -323,7 +325,7 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
enum { enum {
/* PLL O1 O2 O3 VP DV EN TX */ /* PLL O1 O2 O3 VP DV EN TX */
/* 4320 /4 /4 /1 /5 /1 => /2 /2 */ /* 4320 /4 /4 /1 /5 /1 => /2 /2 */
MESON_VCLK_HDMI_ENCI_54000 = 1, MESON_VCLK_HDMI_ENCI_54000 = 0,
/* 4320 /4 /4 /1 /5 /1 => /1 /2 */ /* 4320 /4 /4 /1 /5 /1 => /1 /2 */
MESON_VCLK_HDMI_DDR_54000, MESON_VCLK_HDMI_DDR_54000,
/* 2970 /4 /1 /1 /5 /1 => /1 /2 */ /* 2970 /4 /1 /1 /5 /1 => /1 /2 */
...@@ -339,6 +341,7 @@ enum { ...@@ -339,6 +341,7 @@ enum {
}; };
struct meson_vclk_params { struct meson_vclk_params {
unsigned int pixel_freq;
unsigned int pll_base_freq; unsigned int pll_base_freq;
unsigned int pll_od1; unsigned int pll_od1;
unsigned int pll_od2; unsigned int pll_od2;
...@@ -347,6 +350,7 @@ struct meson_vclk_params { ...@@ -347,6 +350,7 @@ struct meson_vclk_params {
unsigned int vclk_div; unsigned int vclk_div;
} params[] = { } params[] = {
[MESON_VCLK_HDMI_ENCI_54000] = { [MESON_VCLK_HDMI_ENCI_54000] = {
.pixel_freq = 54000,
.pll_base_freq = 4320000, .pll_base_freq = 4320000,
.pll_od1 = 4, .pll_od1 = 4,
.pll_od2 = 4, .pll_od2 = 4,
...@@ -355,6 +359,7 @@ struct meson_vclk_params { ...@@ -355,6 +359,7 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_DDR_54000] = { [MESON_VCLK_HDMI_DDR_54000] = {
.pixel_freq = 54000,
.pll_base_freq = 4320000, .pll_base_freq = 4320000,
.pll_od1 = 4, .pll_od1 = 4,
.pll_od2 = 4, .pll_od2 = 4,
...@@ -363,6 +368,7 @@ struct meson_vclk_params { ...@@ -363,6 +368,7 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_DDR_148500] = { [MESON_VCLK_HDMI_DDR_148500] = {
.pixel_freq = 148500,
.pll_base_freq = 2970000, .pll_base_freq = 2970000,
.pll_od1 = 4, .pll_od1 = 4,
.pll_od2 = 1, .pll_od2 = 1,
...@@ -371,6 +377,7 @@ struct meson_vclk_params { ...@@ -371,6 +377,7 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_74250] = { [MESON_VCLK_HDMI_74250] = {
.pixel_freq = 74250,
.pll_base_freq = 2970000, .pll_base_freq = 2970000,
.pll_od1 = 2, .pll_od1 = 2,
.pll_od2 = 2, .pll_od2 = 2,
...@@ -379,6 +386,7 @@ struct meson_vclk_params { ...@@ -379,6 +386,7 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_148500] = { [MESON_VCLK_HDMI_148500] = {
.pixel_freq = 148500,
.pll_base_freq = 2970000, .pll_base_freq = 2970000,
.pll_od1 = 1, .pll_od1 = 1,
.pll_od2 = 2, .pll_od2 = 2,
...@@ -387,6 +395,7 @@ struct meson_vclk_params { ...@@ -387,6 +395,7 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_297000] = { [MESON_VCLK_HDMI_297000] = {
.pixel_freq = 297000,
.pll_base_freq = 2970000, .pll_base_freq = 2970000,
.pll_od1 = 1, .pll_od1 = 1,
.pll_od2 = 1, .pll_od2 = 1,
...@@ -395,6 +404,7 @@ struct meson_vclk_params { ...@@ -395,6 +404,7 @@ struct meson_vclk_params {
.vclk_div = 2, .vclk_div = 2,
}, },
[MESON_VCLK_HDMI_594000] = { [MESON_VCLK_HDMI_594000] = {
.pixel_freq = 594000,
.pll_base_freq = 5940000, .pll_base_freq = 5940000,
.pll_od1 = 1, .pll_od1 = 1,
.pll_od2 = 1, .pll_od2 = 1,
...@@ -402,6 +412,7 @@ struct meson_vclk_params { ...@@ -402,6 +412,7 @@ struct meson_vclk_params {
.vid_pll_div = VID_PLL_DIV_5, .vid_pll_div = VID_PLL_DIV_5,
.vclk_div = 1, .vclk_div = 1,
}, },
{ /* sentinel */ },
}; };
static inline unsigned int pll_od_to_reg(unsigned int od) static inline unsigned int pll_od_to_reg(unsigned int od)
...@@ -626,12 +637,37 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv, ...@@ -626,12 +637,37 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
pll_freq); pll_freq);
} }
enum drm_mode_status
meson_vclk_vic_supported_freq(unsigned int freq)
{
int i;
DRM_DEBUG_DRIVER("freq = %d\n", freq);
for (i = 0 ; params[i].pixel_freq ; ++i) {
DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n",
i, params[i].pixel_freq,
FREQ_1000_1001(params[i].pixel_freq));
/* Match strict frequency */
if (freq == params[i].pixel_freq)
return MODE_OK;
/* Match 1000/1001 variant */
if (freq == FREQ_1000_1001(params[i].pixel_freq))
return MODE_OK;
}
return MODE_CLOCK_RANGE;
}
EXPORT_SYMBOL_GPL(meson_vclk_vic_supported_freq);
static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_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 od1, unsigned int od2, unsigned int od3,
unsigned int vid_pll_div, unsigned int vclk_div, unsigned int vid_pll_div, unsigned int vclk_div,
unsigned int hdmi_tx_div, unsigned int venc_div, unsigned int hdmi_tx_div, unsigned int venc_div,
bool hdmi_use_enci) bool hdmi_use_enci, bool vic_alternate_clock)
{ {
unsigned int m = 0, frac = 0;
/* Set HDMI-TX sys clock */ /* Set HDMI-TX sys clock */
regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL,
CTS_HDMI_SYS_SEL_MASK, 0); CTS_HDMI_SYS_SEL_MASK, 0);
...@@ -646,34 +682,38 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq, ...@@ -646,34 +682,38 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
switch (pll_base_freq) { switch (pll_base_freq) {
case 2970000: case 2970000:
meson_hdmi_pll_set_params(priv, 0x3d, 0xe00, m = 0x3d;
od1, od2, od3); frac = vic_alternate_clock ? 0xd02 : 0xe00;
break; break;
case 4320000: case 4320000:
meson_hdmi_pll_set_params(priv, 0x5a, 0, m = vic_alternate_clock ? 0x59 : 0x5a;
od1, od2, od3); frac = vic_alternate_clock ? 0xe8f : 0;
break; break;
case 5940000: case 5940000:
meson_hdmi_pll_set_params(priv, 0x7b, 0xc00, m = 0x7b;
od1, od2, od3); frac = vic_alternate_clock ? 0xa05 : 0xc00;
break; break;
} }
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
switch (pll_base_freq) { switch (pll_base_freq) {
case 2970000: case 2970000:
meson_hdmi_pll_set_params(priv, 0x7b, 0x300, m = 0x7b;
od1, od2, od3); frac = vic_alternate_clock ? 0x281 : 0x300;
break; break;
case 4320000: case 4320000:
meson_hdmi_pll_set_params(priv, 0xb4, 0, m = vic_alternate_clock ? 0xb3 : 0xb4;
od1, od2, od3); frac = vic_alternate_clock ? 0x347 : 0;
break; break;
case 5940000: case 5940000:
meson_hdmi_pll_set_params(priv, 0xf7, 0x200, m = 0xf7;
od1, od2, od3); frac = vic_alternate_clock ? 0x102 : 0x200;
break; break;
} }
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
} }
/* Setup vid_pll divider */ /* Setup vid_pll divider */
...@@ -826,6 +866,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, ...@@ -826,6 +866,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
unsigned int vclk_freq, unsigned int venc_freq, unsigned int vclk_freq, unsigned int venc_freq,
unsigned int dac_freq, bool hdmi_use_enci) unsigned int dac_freq, bool hdmi_use_enci)
{ {
bool vic_alternate_clock = false;
unsigned int freq; unsigned int freq;
unsigned int hdmi_tx_div; unsigned int hdmi_tx_div;
unsigned int venc_div; unsigned int venc_div;
...@@ -843,7 +884,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, ...@@ -843,7 +884,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
* - encp encoder * - encp encoder
*/ */
meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0, meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0,
VID_PLL_DIV_5, 2, 1, 1, false); VID_PLL_DIV_5, 2, 1, 1, false, false);
return; return;
} }
...@@ -863,31 +904,35 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, ...@@ -863,31 +904,35 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
return; return;
} }
switch (vclk_freq) { for (freq = 0 ; params[freq].pixel_freq ; ++freq) {
case 54000: if (vclk_freq == params[freq].pixel_freq ||
if (hdmi_use_enci) vclk_freq == FREQ_1000_1001(params[freq].pixel_freq)) {
freq = MESON_VCLK_HDMI_ENCI_54000; if (vclk_freq != params[freq].pixel_freq)
vic_alternate_clock = true;
else else
freq = MESON_VCLK_HDMI_DDR_54000; vic_alternate_clock = false;
break;
case 74250: if (freq == MESON_VCLK_HDMI_ENCI_54000 &&
freq = MESON_VCLK_HDMI_74250; !hdmi_use_enci)
break; continue;
case 148500:
if (dac_freq != 148500) if (freq == MESON_VCLK_HDMI_DDR_54000 &&
freq = MESON_VCLK_HDMI_DDR_148500; hdmi_use_enci)
else continue;
freq = MESON_VCLK_HDMI_148500;
break; if (freq == MESON_VCLK_HDMI_DDR_148500 &&
case 297000: dac_freq == vclk_freq)
freq = MESON_VCLK_HDMI_297000; continue;
break;
case 594000: if (freq == MESON_VCLK_HDMI_148500 &&
freq = MESON_VCLK_HDMI_594000; dac_freq != vclk_freq)
continue;
break; break;
default: }
pr_err("Fatal Error, invalid HDMI vclk freq %d\n", }
vclk_freq);
if (!params[freq].pixel_freq) {
pr_err("Fatal Error, invalid HDMI vclk freq %d\n", vclk_freq);
return; return;
} }
...@@ -895,6 +940,6 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, ...@@ -895,6 +940,6 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
params[freq].pll_od1, params[freq].pll_od2, params[freq].pll_od1, params[freq].pll_od2,
params[freq].pll_od3, params[freq].vid_pll_div, params[freq].pll_od3, params[freq].vid_pll_div,
params[freq].vclk_div, hdmi_tx_div, venc_div, params[freq].vclk_div, hdmi_tx_div, venc_div,
hdmi_use_enci); hdmi_use_enci, vic_alternate_clock);
} }
EXPORT_SYMBOL_GPL(meson_vclk_setup); EXPORT_SYMBOL_GPL(meson_vclk_setup);
...@@ -32,6 +32,8 @@ enum { ...@@ -32,6 +32,8 @@ enum {
enum drm_mode_status enum drm_mode_status
meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq); meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq);
enum drm_mode_status
meson_vclk_vic_supported_freq(unsigned int freq);
void meson_vclk_setup(struct meson_drm *priv, unsigned int target, void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
unsigned int vclk_freq, unsigned int venc_freq, unsigned int vclk_freq, unsigned int venc_freq,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment