Commit e5fab2ec authored by Neil Armstrong's avatar Neil Armstrong

drm/meson: vclk: add support for YUV420 setup

This patch adds clocking support for the YUV420 output from the
Amlogic Meson SoCs Video Processing Unit to the HDMI Controller.

The YUV420 is obtained by generating a YUV444 pixel stream like
the classic HDMI display modes, but then the Video Encoder output
can be configured to down-sample the YUV444 pixel stream to a YUV420
stream.

This mode needs a different clock generation scheme since the TMDS PHY
clock must match the 10x ratio with the YUV420 pixel clock, but
the video encoder must run at 2x the pixel clock.

This patch adds the TMDS PHY clock value in all the video clock setup
in order to better support these specific uses cases and switch
to the Common Clock framework for clocks handling in the future.
Signed-off-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Reviewed-by: default avatarJernej Škrabec <jernej.skrabec@siol.net>
Link: https://patchwork.freedesktop.org/patch/msgid/20200304104052.17196-11-narmstrong@baylibre.com
parent 64db601a
...@@ -376,15 +376,19 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi, ...@@ -376,15 +376,19 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
{ {
struct meson_drm *priv = dw_hdmi->priv; struct meson_drm *priv = dw_hdmi->priv;
int vic = drm_match_cea_mode(mode); int vic = drm_match_cea_mode(mode);
unsigned int phy_freq;
unsigned int vclk_freq; unsigned int vclk_freq;
unsigned int venc_freq; unsigned int venc_freq;
unsigned int hdmi_freq; unsigned int hdmi_freq;
vclk_freq = mode->clock; vclk_freq = mode->clock;
/* TMDS clock is pixel_clock * 10 */
phy_freq = vclk_freq * 10;
if (!vic) { if (!vic) {
meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, vclk_freq, meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, phy_freq,
vclk_freq, vclk_freq, false); vclk_freq, vclk_freq, vclk_freq, false);
return; return;
} }
...@@ -402,11 +406,11 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi, ...@@ -402,11 +406,11 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
if (mode->flags & DRM_MODE_FLAG_DBLCLK) if (mode->flags & DRM_MODE_FLAG_DBLCLK)
venc_freq /= 2; venc_freq /= 2;
DRM_DEBUG_DRIVER("vclk:%d venc=%d hdmi=%d enci=%d\n", DRM_DEBUG_DRIVER("vclk:%d phy=%d venc=%d hdmi=%d enci=%d\n",
vclk_freq, venc_freq, hdmi_freq, phy_freq, vclk_freq, venc_freq, hdmi_freq,
priv->venc.hdmi_use_enci); priv->venc.hdmi_use_enci);
meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, vclk_freq, meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, phy_freq, vclk_freq,
venc_freq, hdmi_freq, priv->venc.hdmi_use_enci); venc_freq, hdmi_freq, priv->venc.hdmi_use_enci);
} }
...@@ -617,6 +621,7 @@ dw_hdmi_mode_valid(struct drm_connector *connector, ...@@ -617,6 +621,7 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode) const struct drm_display_mode *mode)
{ {
struct meson_drm *priv = connector->dev->dev_private; struct meson_drm *priv = connector->dev->dev_private;
unsigned int phy_freq;
unsigned int vclk_freq; unsigned int vclk_freq;
unsigned int venc_freq; unsigned int venc_freq;
unsigned int hdmi_freq; unsigned int hdmi_freq;
...@@ -643,6 +648,9 @@ dw_hdmi_mode_valid(struct drm_connector *connector, ...@@ -643,6 +648,9 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
vclk_freq = mode->clock; vclk_freq = mode->clock;
/* TMDS clock is pixel_clock * 10 */
phy_freq = vclk_freq * 10;
/* 480i/576i needs global pixel doubling */ /* 480i/576i needs global pixel doubling */
if (mode->flags & DRM_MODE_FLAG_DBLCLK) if (mode->flags & DRM_MODE_FLAG_DBLCLK)
vclk_freq *= 2; vclk_freq *= 2;
...@@ -659,10 +667,10 @@ dw_hdmi_mode_valid(struct drm_connector *connector, ...@@ -659,10 +667,10 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
if (mode->flags & DRM_MODE_FLAG_DBLCLK) if (mode->flags & DRM_MODE_FLAG_DBLCLK)
venc_freq /= 2; venc_freq /= 2;
dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__, dev_dbg(connector->dev->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n",
vclk_freq, venc_freq, hdmi_freq); __func__, phy_freq, vclk_freq, venc_freq, hdmi_freq);
return meson_vclk_vic_supported_freq(vclk_freq); return meson_vclk_vic_supported_freq(phy_freq, vclk_freq);
} }
/* Encoder */ /* Encoder */
......
...@@ -354,12 +354,17 @@ enum { ...@@ -354,12 +354,17 @@ enum {
/* 2970 /1 /1 /1 /5 /2 => /1 /1 */ /* 2970 /1 /1 /1 /5 /2 => /1 /1 */
MESON_VCLK_HDMI_297000, MESON_VCLK_HDMI_297000,
/* 5940 /1 /1 /2 /5 /1 => /1 /1 */ /* 5940 /1 /1 /2 /5 /1 => /1 /1 */
MESON_VCLK_HDMI_594000 MESON_VCLK_HDMI_594000,
/* 2970 /1 /1 /1 /5 /1 => /1 /2 */
MESON_VCLK_HDMI_594000_YUV420,
}; };
struct meson_vclk_params { struct meson_vclk_params {
unsigned int pll_freq;
unsigned int phy_freq;
unsigned int vclk_freq;
unsigned int venc_freq;
unsigned int pixel_freq; unsigned int pixel_freq;
unsigned int pll_base_freq;
unsigned int pll_od1; unsigned int pll_od1;
unsigned int pll_od2; unsigned int pll_od2;
unsigned int pll_od3; unsigned int pll_od3;
...@@ -367,8 +372,11 @@ struct meson_vclk_params { ...@@ -367,8 +372,11 @@ struct meson_vclk_params {
unsigned int vclk_div; unsigned int vclk_div;
} params[] = { } params[] = {
[MESON_VCLK_HDMI_ENCI_54000] = { [MESON_VCLK_HDMI_ENCI_54000] = {
.pll_freq = 4320000,
.phy_freq = 270000,
.vclk_freq = 54000,
.venc_freq = 54000,
.pixel_freq = 54000, .pixel_freq = 54000,
.pll_base_freq = 4320000,
.pll_od1 = 4, .pll_od1 = 4,
.pll_od2 = 4, .pll_od2 = 4,
.pll_od3 = 1, .pll_od3 = 1,
...@@ -376,8 +384,11 @@ struct meson_vclk_params { ...@@ -376,8 +384,11 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_DDR_54000] = { [MESON_VCLK_HDMI_DDR_54000] = {
.pixel_freq = 54000, .pll_freq = 4320000,
.pll_base_freq = 4320000, .phy_freq = 270000,
.vclk_freq = 54000,
.venc_freq = 54000,
.pixel_freq = 27000,
.pll_od1 = 4, .pll_od1 = 4,
.pll_od2 = 4, .pll_od2 = 4,
.pll_od3 = 1, .pll_od3 = 1,
...@@ -385,8 +396,11 @@ struct meson_vclk_params { ...@@ -385,8 +396,11 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_DDR_148500] = { [MESON_VCLK_HDMI_DDR_148500] = {
.pixel_freq = 148500, .pll_freq = 2970000,
.pll_base_freq = 2970000, .phy_freq = 742500,
.vclk_freq = 148500,
.venc_freq = 148500,
.pixel_freq = 74250,
.pll_od1 = 4, .pll_od1 = 4,
.pll_od2 = 1, .pll_od2 = 1,
.pll_od3 = 1, .pll_od3 = 1,
...@@ -394,8 +408,11 @@ struct meson_vclk_params { ...@@ -394,8 +408,11 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_74250] = { [MESON_VCLK_HDMI_74250] = {
.pll_freq = 2970000,
.phy_freq = 742500,
.vclk_freq = 74250,
.venc_freq = 74250,
.pixel_freq = 74250, .pixel_freq = 74250,
.pll_base_freq = 2970000,
.pll_od1 = 2, .pll_od1 = 2,
.pll_od2 = 2, .pll_od2 = 2,
.pll_od3 = 2, .pll_od3 = 2,
...@@ -403,8 +420,11 @@ struct meson_vclk_params { ...@@ -403,8 +420,11 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_148500] = { [MESON_VCLK_HDMI_148500] = {
.pll_freq = 2970000,
.phy_freq = 1485000,
.vclk_freq = 148500,
.venc_freq = 148500,
.pixel_freq = 148500, .pixel_freq = 148500,
.pll_base_freq = 2970000,
.pll_od1 = 1, .pll_od1 = 1,
.pll_od2 = 2, .pll_od2 = 2,
.pll_od3 = 2, .pll_od3 = 2,
...@@ -412,8 +432,11 @@ struct meson_vclk_params { ...@@ -412,8 +432,11 @@ struct meson_vclk_params {
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_297000] = { [MESON_VCLK_HDMI_297000] = {
.pll_freq = 5940000,
.phy_freq = 2970000,
.venc_freq = 297000,
.vclk_freq = 297000,
.pixel_freq = 297000, .pixel_freq = 297000,
.pll_base_freq = 5940000,
.pll_od1 = 2, .pll_od1 = 2,
.pll_od2 = 1, .pll_od2 = 1,
.pll_od3 = 1, .pll_od3 = 1,
...@@ -421,14 +444,29 @@ struct meson_vclk_params { ...@@ -421,14 +444,29 @@ struct meson_vclk_params {
.vclk_div = 2, .vclk_div = 2,
}, },
[MESON_VCLK_HDMI_594000] = { [MESON_VCLK_HDMI_594000] = {
.pll_freq = 5940000,
.phy_freq = 5940000,
.venc_freq = 594000,
.vclk_freq = 594000,
.pixel_freq = 594000, .pixel_freq = 594000,
.pll_base_freq = 5940000,
.pll_od1 = 1, .pll_od1 = 1,
.pll_od2 = 1, .pll_od2 = 1,
.pll_od3 = 2, .pll_od3 = 2,
.vid_pll_div = VID_PLL_DIV_5, .vid_pll_div = VID_PLL_DIV_5,
.vclk_div = 1, .vclk_div = 1,
}, },
[MESON_VCLK_HDMI_594000_YUV420] = {
.pll_freq = 5940000,
.phy_freq = 2970000,
.venc_freq = 594000,
.vclk_freq = 594000,
.pixel_freq = 297000,
.pll_od1 = 2,
.pll_od2 = 1,
.pll_od3 = 1,
.vid_pll_div = VID_PLL_DIV_5,
.vclk_div = 1,
},
{ /* sentinel */ }, { /* sentinel */ },
}; };
...@@ -701,6 +739,7 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv, ...@@ -701,6 +739,7 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
unsigned int od, m, frac, od1, od2, od3; unsigned int od, m, frac, od1, od2, od3;
if (meson_hdmi_pll_find_params(priv, pll_freq, &m, &frac, &od)) { if (meson_hdmi_pll_find_params(priv, pll_freq, &m, &frac, &od)) {
/* OD2 goes to the PHY, and needs to be *10, so keep OD3=1 */
od3 = 1; od3 = 1;
if (od < 4) { if (od < 4) {
od1 = 2; od1 = 2;
...@@ -723,21 +762,28 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv, ...@@ -723,21 +762,28 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
} }
enum drm_mode_status enum drm_mode_status
meson_vclk_vic_supported_freq(unsigned int freq) meson_vclk_vic_supported_freq(unsigned int phy_freq,
unsigned int vclk_freq)
{ {
int i; int i;
DRM_DEBUG_DRIVER("freq = %d\n", freq); DRM_DEBUG_DRIVER("phy_freq = %d vclk_freq = %d\n",
phy_freq, vclk_freq);
for (i = 0 ; params[i].pixel_freq ; ++i) { for (i = 0 ; params[i].pixel_freq ; ++i) {
DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n", DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n",
i, params[i].pixel_freq, i, params[i].pixel_freq,
FREQ_1000_1001(params[i].pixel_freq)); FREQ_1000_1001(params[i].pixel_freq));
DRM_DEBUG_DRIVER("i = %d phy_freq = %d alt = %d\n",
i, params[i].phy_freq,
FREQ_1000_1001(params[i].phy_freq/10)*10);
/* Match strict frequency */ /* Match strict frequency */
if (freq == params[i].pixel_freq) if (phy_freq == params[i].phy_freq &&
vclk_freq == params[i].vclk_freq)
return MODE_OK; return MODE_OK;
/* Match 1000/1001 variant */ /* Match 1000/1001 variant */
if (freq == FREQ_1000_1001(params[i].pixel_freq)) if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/10)*10) &&
vclk_freq == FREQ_1000_1001(params[i].vclk_freq))
return MODE_OK; return MODE_OK;
} }
...@@ -965,8 +1011,9 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq, ...@@ -965,8 +1011,9 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_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 phy_freq, unsigned int vclk_freq,
unsigned int dac_freq, bool hdmi_use_enci) unsigned int venc_freq, unsigned int dac_freq,
bool hdmi_use_enci)
{ {
bool vic_alternate_clock = false; bool vic_alternate_clock = false;
unsigned int freq; unsigned int freq;
...@@ -986,7 +1033,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, ...@@ -986,7 +1033,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
* - venc_div = 1 * - venc_div = 1
* - encp encoder * - encp encoder
*/ */
meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0, meson_vclk_set(priv, phy_freq, 0, 0, 0,
VID_PLL_DIV_5, 2, 1, 1, false, false); VID_PLL_DIV_5, 2, 1, 1, false, false);
return; return;
} }
...@@ -1008,9 +1055,11 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, ...@@ -1008,9 +1055,11 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
} }
for (freq = 0 ; params[freq].pixel_freq ; ++freq) { for (freq = 0 ; params[freq].pixel_freq ; ++freq) {
if (vclk_freq == params[freq].pixel_freq || if ((phy_freq == params[freq].phy_freq ||
vclk_freq == FREQ_1000_1001(params[freq].pixel_freq)) { phy_freq == FREQ_1000_1001(params[freq].phy_freq/10)*10) &&
if (vclk_freq != params[freq].pixel_freq) (vclk_freq == params[freq].vclk_freq ||
vclk_freq == FREQ_1000_1001(params[freq].vclk_freq))) {
if (vclk_freq != params[freq].vclk_freq)
vic_alternate_clock = true; vic_alternate_clock = true;
else else
vic_alternate_clock = false; vic_alternate_clock = false;
...@@ -1039,7 +1088,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, ...@@ -1039,7 +1088,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
return; return;
} }
meson_vclk_set(priv, params[freq].pll_base_freq, meson_vclk_set(priv, params[freq].pll_freq,
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,
......
...@@ -25,10 +25,11 @@ enum { ...@@ -25,10 +25,11 @@ 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 enum drm_mode_status
meson_vclk_vic_supported_freq(unsigned int freq); meson_vclk_vic_supported_freq(unsigned int phy_freq, unsigned int vclk_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 phy_freq, unsigned int vclk_freq,
unsigned int dac_freq, bool hdmi_use_enci); unsigned int venc_freq, unsigned int dac_freq,
bool hdmi_use_enci);
#endif /* __MESON_VCLK_H */ #endif /* __MESON_VCLK_H */
...@@ -213,8 +213,10 @@ static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder, ...@@ -213,8 +213,10 @@ static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder,
meson_venci_cvbs_mode_set(priv, meson_mode->enci); meson_venci_cvbs_mode_set(priv, meson_mode->enci);
/* Setup 27MHz vclk2 for ENCI and VDAC */ /* Setup 27MHz vclk2 for ENCI and VDAC */
meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, MESON_VCLK_CVBS, meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS,
MESON_VCLK_CVBS, MESON_VCLK_CVBS, true); MESON_VCLK_CVBS, MESON_VCLK_CVBS,
MESON_VCLK_CVBS, MESON_VCLK_CVBS,
true);
} }
} }
......
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