mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-30 21:52:21 +00:00
drm/meson: use unsigned long long / Hz for frequency types
Christian reports that 4K output using YUV420 encoding fails with the
following error:
Fatal Error, invalid HDMI vclk freq 593406
Modetest shows the following:
3840x2160 59.94 3840 4016 4104 4400 2160 2168 2178 2250 593407 flags: xxxx, xxxx,
drm calculated value -------------------------------------^
This indicates that there's a (1kHz) mismatch between the clock
calculated by the drm framework and the meson driver.
Relevant function call stack:
(drm framework)
-> meson_encoder_hdmi_atomic_enable()
-> meson_encoder_hdmi_set_vclk()
-> meson_vclk_setup()
The video clock requested by the drm framework is 593407kHz. This is
passed by meson_encoder_hdmi_atomic_enable() to
meson_encoder_hdmi_set_vclk() and the following formula is applied:
- the frequency is halved (which would be 296703.5kHz) and rounded down
to the next full integer, which is 296703kHz
- TMDS clock is calculated (296703kHz * 10)
- video encoder clock is calculated - this needs to match a table from
meson_vclk.c and so it doubles the previously halved value again
(resulting in 593406kHz)
- meson_vclk_setup() can't find (either directly, or by deriving it from
594000kHz * 1000 / 1001 and rounding to the closest integer value -
which is 593407kHz as originally requested by the drm framework) a
matching clock in it's internal table and errors out with "invalid
HDMI vclk freq"
Fix the division precision by switching the whole meson driver to use
unsigned long long (64-bit) Hz values for clock frequencies instead of
unsigned int (32-bit) kHz to fix the rouding error.
Fixes: e5fab2ec9c
("drm/meson: vclk: add support for YUV420 setup")
Reported-by: Christian Hewitt <christianshewitt@gmail.com>
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250421201300.778955-3-martin.blumenstingl@googlemail.com
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://lore.kernel.org/r/20250421201300.778955-3-martin.blumenstingl@googlemail.com
This commit is contained in:
parent
f37bb5486e
commit
1017560164
@ -169,7 +169,7 @@ static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = {
|
|||||||
/* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */
|
/* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */
|
||||||
{
|
{
|
||||||
.limits = {
|
.limits = {
|
||||||
.max_hdmi_phy_freq = 1650000,
|
.max_hdmi_phy_freq = 1650000000,
|
||||||
},
|
},
|
||||||
.attrs = (const struct soc_device_attribute []) {
|
.attrs = (const struct soc_device_attribute []) {
|
||||||
{ .soc_id = "GXL (S805*)", },
|
{ .soc_id = "GXL (S805*)", },
|
||||||
|
@ -37,7 +37,7 @@ struct meson_drm_match_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct meson_drm_soc_limits {
|
struct meson_drm_soc_limits {
|
||||||
unsigned int max_hdmi_phy_freq;
|
unsigned long long max_hdmi_phy_freq;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct meson_drm {
|
struct meson_drm {
|
||||||
|
@ -70,12 +70,12 @@ static void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi,
|
|||||||
{
|
{
|
||||||
struct meson_drm *priv = encoder_hdmi->priv;
|
struct meson_drm *priv = encoder_hdmi->priv;
|
||||||
int vic = drm_match_cea_mode(mode);
|
int vic = drm_match_cea_mode(mode);
|
||||||
unsigned int phy_freq;
|
unsigned long long phy_freq;
|
||||||
unsigned int vclk_freq;
|
unsigned long long vclk_freq;
|
||||||
unsigned int venc_freq;
|
unsigned long long venc_freq;
|
||||||
unsigned int hdmi_freq;
|
unsigned long long hdmi_freq;
|
||||||
|
|
||||||
vclk_freq = mode->clock;
|
vclk_freq = mode->clock * 1000;
|
||||||
|
|
||||||
/* For 420, pixel clock is half unlike venc clock */
|
/* For 420, pixel clock is half unlike venc clock */
|
||||||
if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
|
if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
|
||||||
@ -107,7 +107,8 @@ static void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi,
|
|||||||
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
||||||
venc_freq /= 2;
|
venc_freq /= 2;
|
||||||
|
|
||||||
dev_dbg(priv->dev, "vclk:%d phy=%d venc=%d hdmi=%d enci=%d\n",
|
dev_dbg(priv->dev,
|
||||||
|
"vclk:%lluHz phy=%lluHz venc=%lluHz hdmi=%lluHz enci=%d\n",
|
||||||
phy_freq, vclk_freq, venc_freq, hdmi_freq,
|
phy_freq, vclk_freq, venc_freq, hdmi_freq,
|
||||||
priv->venc.hdmi_use_enci);
|
priv->venc.hdmi_use_enci);
|
||||||
|
|
||||||
@ -122,10 +123,11 @@ static enum drm_mode_status meson_encoder_hdmi_mode_valid(struct drm_bridge *bri
|
|||||||
struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
|
struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
|
||||||
struct meson_drm *priv = encoder_hdmi->priv;
|
struct meson_drm *priv = encoder_hdmi->priv;
|
||||||
bool is_hdmi2_sink = display_info->hdmi.scdc.supported;
|
bool is_hdmi2_sink = display_info->hdmi.scdc.supported;
|
||||||
unsigned int phy_freq;
|
unsigned long long clock = mode->clock * 1000;
|
||||||
unsigned int vclk_freq;
|
unsigned long long phy_freq;
|
||||||
unsigned int venc_freq;
|
unsigned long long vclk_freq;
|
||||||
unsigned int hdmi_freq;
|
unsigned long long venc_freq;
|
||||||
|
unsigned long long hdmi_freq;
|
||||||
int vic = drm_match_cea_mode(mode);
|
int vic = drm_match_cea_mode(mode);
|
||||||
enum drm_mode_status status;
|
enum drm_mode_status status;
|
||||||
|
|
||||||
@ -144,12 +146,12 @@ static enum drm_mode_status meson_encoder_hdmi_mode_valid(struct drm_bridge *bri
|
|||||||
if (status != MODE_OK)
|
if (status != MODE_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
return meson_vclk_dmt_supported_freq(priv, mode->clock);
|
return meson_vclk_dmt_supported_freq(priv, clock);
|
||||||
/* Check against supported VIC modes */
|
/* Check against supported VIC modes */
|
||||||
} else if (!meson_venc_hdmi_supported_vic(vic))
|
} else if (!meson_venc_hdmi_supported_vic(vic))
|
||||||
return MODE_BAD;
|
return MODE_BAD;
|
||||||
|
|
||||||
vclk_freq = mode->clock;
|
vclk_freq = clock;
|
||||||
|
|
||||||
/* For 420, pixel clock is half unlike venc clock */
|
/* For 420, pixel clock is half unlike venc clock */
|
||||||
if (drm_mode_is_420_only(display_info, mode) ||
|
if (drm_mode_is_420_only(display_info, mode) ||
|
||||||
@ -179,7 +181,8 @@ static enum drm_mode_status meson_encoder_hdmi_mode_valid(struct drm_bridge *bri
|
|||||||
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
||||||
venc_freq /= 2;
|
venc_freq /= 2;
|
||||||
|
|
||||||
dev_dbg(priv->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n",
|
dev_dbg(priv->dev,
|
||||||
|
"%s: vclk:%lluHz phy=%lluHz venc=%lluHz hdmi=%lluHz\n",
|
||||||
__func__, phy_freq, vclk_freq, venc_freq, hdmi_freq);
|
__func__, phy_freq, vclk_freq, venc_freq, hdmi_freq);
|
||||||
|
|
||||||
return meson_vclk_vic_supported_freq(priv, phy_freq, vclk_freq);
|
return meson_vclk_vic_supported_freq(priv, phy_freq, vclk_freq);
|
||||||
|
@ -110,7 +110,10 @@
|
|||||||
#define HDMI_PLL_LOCK BIT(31)
|
#define HDMI_PLL_LOCK BIT(31)
|
||||||
#define HDMI_PLL_LOCK_G12A (3 << 30)
|
#define HDMI_PLL_LOCK_G12A (3 << 30)
|
||||||
|
|
||||||
#define FREQ_1000_1001(_freq) DIV_ROUND_CLOSEST(_freq * 1000, 1001)
|
#define PIXEL_FREQ_1000_1001(_freq) \
|
||||||
|
DIV_ROUND_CLOSEST_ULL((_freq) * 1000ULL, 1001ULL)
|
||||||
|
#define PHY_FREQ_1000_1001(_freq) \
|
||||||
|
(PIXEL_FREQ_1000_1001(DIV_ROUND_DOWN_ULL(_freq, 10ULL)) * 10)
|
||||||
|
|
||||||
/* VID PLL Dividers */
|
/* VID PLL Dividers */
|
||||||
enum {
|
enum {
|
||||||
@ -360,11 +363,11 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct meson_vclk_params {
|
struct meson_vclk_params {
|
||||||
unsigned int pll_freq;
|
unsigned long long pll_freq;
|
||||||
unsigned int phy_freq;
|
unsigned long long phy_freq;
|
||||||
unsigned int vclk_freq;
|
unsigned long long vclk_freq;
|
||||||
unsigned int venc_freq;
|
unsigned long long venc_freq;
|
||||||
unsigned int pixel_freq;
|
unsigned long long pixel_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;
|
||||||
@ -372,11 +375,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,
|
.pll_freq = 4320000000,
|
||||||
.phy_freq = 270000,
|
.phy_freq = 270000000,
|
||||||
.vclk_freq = 54000,
|
.vclk_freq = 54000000,
|
||||||
.venc_freq = 54000,
|
.venc_freq = 54000000,
|
||||||
.pixel_freq = 54000,
|
.pixel_freq = 54000000,
|
||||||
.pll_od1 = 4,
|
.pll_od1 = 4,
|
||||||
.pll_od2 = 4,
|
.pll_od2 = 4,
|
||||||
.pll_od3 = 1,
|
.pll_od3 = 1,
|
||||||
@ -384,11 +387,11 @@ struct meson_vclk_params {
|
|||||||
.vclk_div = 1,
|
.vclk_div = 1,
|
||||||
},
|
},
|
||||||
[MESON_VCLK_HDMI_DDR_54000] = {
|
[MESON_VCLK_HDMI_DDR_54000] = {
|
||||||
.pll_freq = 4320000,
|
.pll_freq = 4320000000,
|
||||||
.phy_freq = 270000,
|
.phy_freq = 270000000,
|
||||||
.vclk_freq = 54000,
|
.vclk_freq = 54000000,
|
||||||
.venc_freq = 54000,
|
.venc_freq = 54000000,
|
||||||
.pixel_freq = 27000,
|
.pixel_freq = 27000000,
|
||||||
.pll_od1 = 4,
|
.pll_od1 = 4,
|
||||||
.pll_od2 = 4,
|
.pll_od2 = 4,
|
||||||
.pll_od3 = 1,
|
.pll_od3 = 1,
|
||||||
@ -396,11 +399,11 @@ struct meson_vclk_params {
|
|||||||
.vclk_div = 1,
|
.vclk_div = 1,
|
||||||
},
|
},
|
||||||
[MESON_VCLK_HDMI_DDR_148500] = {
|
[MESON_VCLK_HDMI_DDR_148500] = {
|
||||||
.pll_freq = 2970000,
|
.pll_freq = 2970000000,
|
||||||
.phy_freq = 742500,
|
.phy_freq = 742500000,
|
||||||
.vclk_freq = 148500,
|
.vclk_freq = 148500000,
|
||||||
.venc_freq = 148500,
|
.venc_freq = 148500000,
|
||||||
.pixel_freq = 74250,
|
.pixel_freq = 74250000,
|
||||||
.pll_od1 = 4,
|
.pll_od1 = 4,
|
||||||
.pll_od2 = 1,
|
.pll_od2 = 1,
|
||||||
.pll_od3 = 1,
|
.pll_od3 = 1,
|
||||||
@ -408,11 +411,11 @@ struct meson_vclk_params {
|
|||||||
.vclk_div = 1,
|
.vclk_div = 1,
|
||||||
},
|
},
|
||||||
[MESON_VCLK_HDMI_74250] = {
|
[MESON_VCLK_HDMI_74250] = {
|
||||||
.pll_freq = 2970000,
|
.pll_freq = 2970000000,
|
||||||
.phy_freq = 742500,
|
.phy_freq = 742500000,
|
||||||
.vclk_freq = 74250,
|
.vclk_freq = 74250000,
|
||||||
.venc_freq = 74250,
|
.venc_freq = 74250000,
|
||||||
.pixel_freq = 74250,
|
.pixel_freq = 74250000,
|
||||||
.pll_od1 = 2,
|
.pll_od1 = 2,
|
||||||
.pll_od2 = 2,
|
.pll_od2 = 2,
|
||||||
.pll_od3 = 2,
|
.pll_od3 = 2,
|
||||||
@ -420,11 +423,11 @@ struct meson_vclk_params {
|
|||||||
.vclk_div = 1,
|
.vclk_div = 1,
|
||||||
},
|
},
|
||||||
[MESON_VCLK_HDMI_148500] = {
|
[MESON_VCLK_HDMI_148500] = {
|
||||||
.pll_freq = 2970000,
|
.pll_freq = 2970000000,
|
||||||
.phy_freq = 1485000,
|
.phy_freq = 1485000000,
|
||||||
.vclk_freq = 148500,
|
.vclk_freq = 148500000,
|
||||||
.venc_freq = 148500,
|
.venc_freq = 148500000,
|
||||||
.pixel_freq = 148500,
|
.pixel_freq = 148500000,
|
||||||
.pll_od1 = 1,
|
.pll_od1 = 1,
|
||||||
.pll_od2 = 2,
|
.pll_od2 = 2,
|
||||||
.pll_od3 = 2,
|
.pll_od3 = 2,
|
||||||
@ -432,11 +435,11 @@ struct meson_vclk_params {
|
|||||||
.vclk_div = 1,
|
.vclk_div = 1,
|
||||||
},
|
},
|
||||||
[MESON_VCLK_HDMI_297000] = {
|
[MESON_VCLK_HDMI_297000] = {
|
||||||
.pll_freq = 5940000,
|
.pll_freq = 5940000000,
|
||||||
.phy_freq = 2970000,
|
.phy_freq = 2970000000,
|
||||||
.venc_freq = 297000,
|
.venc_freq = 297000000,
|
||||||
.vclk_freq = 297000,
|
.vclk_freq = 297000000,
|
||||||
.pixel_freq = 297000,
|
.pixel_freq = 297000000,
|
||||||
.pll_od1 = 2,
|
.pll_od1 = 2,
|
||||||
.pll_od2 = 1,
|
.pll_od2 = 1,
|
||||||
.pll_od3 = 1,
|
.pll_od3 = 1,
|
||||||
@ -444,11 +447,11 @@ struct meson_vclk_params {
|
|||||||
.vclk_div = 2,
|
.vclk_div = 2,
|
||||||
},
|
},
|
||||||
[MESON_VCLK_HDMI_594000] = {
|
[MESON_VCLK_HDMI_594000] = {
|
||||||
.pll_freq = 5940000,
|
.pll_freq = 5940000000,
|
||||||
.phy_freq = 5940000,
|
.phy_freq = 5940000000,
|
||||||
.venc_freq = 594000,
|
.venc_freq = 594000000,
|
||||||
.vclk_freq = 594000,
|
.vclk_freq = 594000000,
|
||||||
.pixel_freq = 594000,
|
.pixel_freq = 594000000,
|
||||||
.pll_od1 = 1,
|
.pll_od1 = 1,
|
||||||
.pll_od2 = 1,
|
.pll_od2 = 1,
|
||||||
.pll_od3 = 2,
|
.pll_od3 = 2,
|
||||||
@ -456,11 +459,11 @@ struct meson_vclk_params {
|
|||||||
.vclk_div = 1,
|
.vclk_div = 1,
|
||||||
},
|
},
|
||||||
[MESON_VCLK_HDMI_594000_YUV420] = {
|
[MESON_VCLK_HDMI_594000_YUV420] = {
|
||||||
.pll_freq = 5940000,
|
.pll_freq = 5940000000,
|
||||||
.phy_freq = 2970000,
|
.phy_freq = 2970000000,
|
||||||
.venc_freq = 594000,
|
.venc_freq = 594000000,
|
||||||
.vclk_freq = 594000,
|
.vclk_freq = 594000000,
|
||||||
.pixel_freq = 297000,
|
.pixel_freq = 297000000,
|
||||||
.pll_od1 = 2,
|
.pll_od1 = 2,
|
||||||
.pll_od2 = 1,
|
.pll_od2 = 1,
|
||||||
.pll_od3 = 1,
|
.pll_od3 = 1,
|
||||||
@ -617,16 +620,16 @@ static void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
|
|||||||
3 << 20, pll_od_to_reg(od3) << 20);
|
3 << 20, pll_od_to_reg(od3) << 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define XTAL_FREQ 24000
|
#define XTAL_FREQ (24 * 1000 * 1000)
|
||||||
|
|
||||||
static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv,
|
static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv,
|
||||||
unsigned int pll_freq)
|
unsigned long long pll_freq)
|
||||||
{
|
{
|
||||||
/* The GXBB PLL has a /2 pre-multiplier */
|
/* The GXBB PLL has a /2 pre-multiplier */
|
||||||
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB))
|
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB))
|
||||||
pll_freq /= 2;
|
pll_freq = DIV_ROUND_DOWN_ULL(pll_freq, 2);
|
||||||
|
|
||||||
return pll_freq / XTAL_FREQ;
|
return DIV_ROUND_DOWN_ULL(pll_freq, XTAL_FREQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HDMI_FRAC_MAX_GXBB 4096
|
#define HDMI_FRAC_MAX_GXBB 4096
|
||||||
@ -635,12 +638,13 @@ static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv,
|
|||||||
|
|
||||||
static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
|
static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
|
||||||
unsigned int m,
|
unsigned int m,
|
||||||
unsigned int pll_freq)
|
unsigned long long pll_freq)
|
||||||
{
|
{
|
||||||
unsigned int parent_freq = XTAL_FREQ;
|
unsigned long long parent_freq = XTAL_FREQ;
|
||||||
unsigned int frac_max = HDMI_FRAC_MAX_GXL;
|
unsigned int frac_max = HDMI_FRAC_MAX_GXL;
|
||||||
unsigned int frac_m;
|
unsigned int frac_m;
|
||||||
unsigned int frac;
|
unsigned int frac;
|
||||||
|
u32 remainder;
|
||||||
|
|
||||||
/* The GXBB PLL has a /2 pre-multiplier and a larger FRAC width */
|
/* The GXBB PLL has a /2 pre-multiplier and a larger FRAC width */
|
||||||
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
|
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
|
||||||
@ -652,11 +656,11 @@ static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
|
|||||||
frac_max = HDMI_FRAC_MAX_G12A;
|
frac_max = HDMI_FRAC_MAX_G12A;
|
||||||
|
|
||||||
/* We can have a perfect match !*/
|
/* We can have a perfect match !*/
|
||||||
if (pll_freq / m == parent_freq &&
|
if (div_u64_rem(pll_freq, m, &remainder) == parent_freq &&
|
||||||
pll_freq % m == 0)
|
remainder == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
frac = div_u64((u64)pll_freq * (u64)frac_max, parent_freq);
|
frac = mul_u64_u64_div_u64(pll_freq, frac_max, parent_freq);
|
||||||
frac_m = m * frac_max;
|
frac_m = m * frac_max;
|
||||||
if (frac_m > frac)
|
if (frac_m > frac)
|
||||||
return frac_max;
|
return frac_max;
|
||||||
@ -666,7 +670,7 @@ static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool meson_hdmi_pll_validate_params(struct meson_drm *priv,
|
static bool meson_hdmi_pll_validate_params(struct meson_drm *priv,
|
||||||
unsigned int m,
|
unsigned long long m,
|
||||||
unsigned int frac)
|
unsigned int frac)
|
||||||
{
|
{
|
||||||
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
|
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
|
||||||
@ -694,7 +698,7 @@ static bool meson_hdmi_pll_validate_params(struct meson_drm *priv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool meson_hdmi_pll_find_params(struct meson_drm *priv,
|
static bool meson_hdmi_pll_find_params(struct meson_drm *priv,
|
||||||
unsigned int freq,
|
unsigned long long freq,
|
||||||
unsigned int *m,
|
unsigned int *m,
|
||||||
unsigned int *frac,
|
unsigned int *frac,
|
||||||
unsigned int *od)
|
unsigned int *od)
|
||||||
@ -706,7 +710,7 @@ static bool meson_hdmi_pll_find_params(struct meson_drm *priv,
|
|||||||
continue;
|
continue;
|
||||||
*frac = meson_hdmi_pll_get_frac(priv, *m, freq * *od);
|
*frac = meson_hdmi_pll_get_frac(priv, *m, freq * *od);
|
||||||
|
|
||||||
DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d\n",
|
DRM_DEBUG_DRIVER("PLL params for %lluHz: m=%x frac=%x od=%d\n",
|
||||||
freq, *m, *frac, *od);
|
freq, *m, *frac, *od);
|
||||||
|
|
||||||
if (meson_hdmi_pll_validate_params(priv, *m, *frac))
|
if (meson_hdmi_pll_validate_params(priv, *m, *frac))
|
||||||
@ -718,7 +722,7 @@ static bool meson_hdmi_pll_find_params(struct meson_drm *priv,
|
|||||||
|
|
||||||
/* pll_freq is the frequency after the OD dividers */
|
/* pll_freq is the frequency after the OD dividers */
|
||||||
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 long long freq)
|
||||||
{
|
{
|
||||||
unsigned int od, m, frac;
|
unsigned int od, m, frac;
|
||||||
|
|
||||||
@ -741,7 +745,7 @@ EXPORT_SYMBOL_GPL(meson_vclk_dmt_supported_freq);
|
|||||||
|
|
||||||
/* pll_freq is the frequency after the OD dividers */
|
/* pll_freq is the frequency after the OD dividers */
|
||||||
static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
|
static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
|
||||||
unsigned int pll_freq)
|
unsigned long long pll_freq)
|
||||||
{
|
{
|
||||||
unsigned int od, m, frac, od1, od2, od3;
|
unsigned int od, m, frac, od1, od2, od3;
|
||||||
|
|
||||||
@ -756,7 +760,7 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
|
|||||||
od1 = od / od2;
|
od1 = od / od2;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRM_DEBUG_DRIVER("PLL params for %dkHz: m=%x frac=%x od=%d/%d/%d\n",
|
DRM_DEBUG_DRIVER("PLL params for %lluHz: m=%x frac=%x od=%d/%d/%d\n",
|
||||||
pll_freq, m, frac, od1, od2, od3);
|
pll_freq, m, frac, od1, od2, od3);
|
||||||
|
|
||||||
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
|
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
|
||||||
@ -764,17 +768,18 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRM_ERROR("Fatal, unable to find parameters for PLL freq %d\n",
|
DRM_ERROR("Fatal, unable to find parameters for PLL freq %lluHz\n",
|
||||||
pll_freq);
|
pll_freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum drm_mode_status
|
enum drm_mode_status
|
||||||
meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
|
meson_vclk_vic_supported_freq(struct meson_drm *priv,
|
||||||
unsigned int vclk_freq)
|
unsigned long long phy_freq,
|
||||||
|
unsigned long long vclk_freq)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
DRM_DEBUG_DRIVER("phy_freq = %d vclk_freq = %d\n",
|
DRM_DEBUG_DRIVER("phy_freq = %lluHz vclk_freq = %lluHz\n",
|
||||||
phy_freq, vclk_freq);
|
phy_freq, vclk_freq);
|
||||||
|
|
||||||
/* Check against soc revision/package limits */
|
/* Check against soc revision/package limits */
|
||||||
@ -785,19 +790,19 @@ meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_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 = %lluHz alt = %lluHz\n",
|
||||||
i, params[i].pixel_freq,
|
i, params[i].pixel_freq,
|
||||||
FREQ_1000_1001(params[i].pixel_freq));
|
PIXEL_FREQ_1000_1001(params[i].pixel_freq));
|
||||||
DRM_DEBUG_DRIVER("i = %d phy_freq = %d alt = %d\n",
|
DRM_DEBUG_DRIVER("i = %d phy_freq = %lluHz alt = %lluHz\n",
|
||||||
i, params[i].phy_freq,
|
i, params[i].phy_freq,
|
||||||
FREQ_1000_1001(params[i].phy_freq/10)*10);
|
PHY_FREQ_1000_1001(params[i].phy_freq));
|
||||||
/* Match strict frequency */
|
/* Match strict frequency */
|
||||||
if (phy_freq == params[i].phy_freq &&
|
if (phy_freq == params[i].phy_freq &&
|
||||||
vclk_freq == params[i].vclk_freq)
|
vclk_freq == params[i].vclk_freq)
|
||||||
return MODE_OK;
|
return MODE_OK;
|
||||||
/* Match 1000/1001 variant */
|
/* Match 1000/1001 variant */
|
||||||
if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/10)*10) &&
|
if (phy_freq == PHY_FREQ_1000_1001(params[i].phy_freq) &&
|
||||||
vclk_freq == FREQ_1000_1001(params[i].vclk_freq))
|
vclk_freq == PIXEL_FREQ_1000_1001(params[i].vclk_freq))
|
||||||
return MODE_OK;
|
return MODE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,8 +810,9 @@ meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(meson_vclk_vic_supported_freq);
|
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 od1, unsigned int od2, unsigned int od3,
|
unsigned long long pll_base_freq, 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 vic_alternate_clock)
|
bool hdmi_use_enci, bool vic_alternate_clock)
|
||||||
@ -826,15 +832,15 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
|
|||||||
meson_hdmi_pll_generic_set(priv, pll_base_freq);
|
meson_hdmi_pll_generic_set(priv, pll_base_freq);
|
||||||
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
|
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
|
||||||
switch (pll_base_freq) {
|
switch (pll_base_freq) {
|
||||||
case 2970000:
|
case 2970000000:
|
||||||
m = 0x3d;
|
m = 0x3d;
|
||||||
frac = vic_alternate_clock ? 0xd02 : 0xe00;
|
frac = vic_alternate_clock ? 0xd02 : 0xe00;
|
||||||
break;
|
break;
|
||||||
case 4320000:
|
case 4320000000:
|
||||||
m = vic_alternate_clock ? 0x59 : 0x5a;
|
m = vic_alternate_clock ? 0x59 : 0x5a;
|
||||||
frac = vic_alternate_clock ? 0xe8f : 0;
|
frac = vic_alternate_clock ? 0xe8f : 0;
|
||||||
break;
|
break;
|
||||||
case 5940000:
|
case 5940000000:
|
||||||
m = 0x7b;
|
m = 0x7b;
|
||||||
frac = vic_alternate_clock ? 0xa05 : 0xc00;
|
frac = vic_alternate_clock ? 0xa05 : 0xc00;
|
||||||
break;
|
break;
|
||||||
@ -844,15 +850,15 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
|
|||||||
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
|
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
|
||||||
meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
|
meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
|
||||||
switch (pll_base_freq) {
|
switch (pll_base_freq) {
|
||||||
case 2970000:
|
case 2970000000:
|
||||||
m = 0x7b;
|
m = 0x7b;
|
||||||
frac = vic_alternate_clock ? 0x281 : 0x300;
|
frac = vic_alternate_clock ? 0x281 : 0x300;
|
||||||
break;
|
break;
|
||||||
case 4320000:
|
case 4320000000:
|
||||||
m = vic_alternate_clock ? 0xb3 : 0xb4;
|
m = vic_alternate_clock ? 0xb3 : 0xb4;
|
||||||
frac = vic_alternate_clock ? 0x347 : 0;
|
frac = vic_alternate_clock ? 0x347 : 0;
|
||||||
break;
|
break;
|
||||||
case 5940000:
|
case 5940000000:
|
||||||
m = 0xf7;
|
m = 0xf7;
|
||||||
frac = vic_alternate_clock ? 0x102 : 0x200;
|
frac = vic_alternate_clock ? 0x102 : 0x200;
|
||||||
break;
|
break;
|
||||||
@ -861,15 +867,15 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
|
|||||||
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
|
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
|
||||||
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
|
} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
|
||||||
switch (pll_base_freq) {
|
switch (pll_base_freq) {
|
||||||
case 2970000:
|
case 2970000000:
|
||||||
m = 0x7b;
|
m = 0x7b;
|
||||||
frac = vic_alternate_clock ? 0x140b4 : 0x18000;
|
frac = vic_alternate_clock ? 0x140b4 : 0x18000;
|
||||||
break;
|
break;
|
||||||
case 4320000:
|
case 4320000000:
|
||||||
m = vic_alternate_clock ? 0xb3 : 0xb4;
|
m = vic_alternate_clock ? 0xb3 : 0xb4;
|
||||||
frac = vic_alternate_clock ? 0x1a3ee : 0;
|
frac = vic_alternate_clock ? 0x1a3ee : 0;
|
||||||
break;
|
break;
|
||||||
case 5940000:
|
case 5940000000:
|
||||||
m = 0xf7;
|
m = 0xf7;
|
||||||
frac = vic_alternate_clock ? 0x8148 : 0x10000;
|
frac = vic_alternate_clock ? 0x8148 : 0x10000;
|
||||||
break;
|
break;
|
||||||
@ -1025,14 +1031,14 @@ 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 phy_freq, unsigned int vclk_freq,
|
unsigned long long phy_freq, unsigned long long vclk_freq,
|
||||||
unsigned int venc_freq, unsigned int dac_freq,
|
unsigned long long venc_freq, unsigned long long dac_freq,
|
||||||
bool hdmi_use_enci)
|
bool hdmi_use_enci)
|
||||||
{
|
{
|
||||||
bool vic_alternate_clock = false;
|
bool vic_alternate_clock = false;
|
||||||
unsigned int freq;
|
unsigned long long freq;
|
||||||
unsigned int hdmi_tx_div;
|
unsigned long long hdmi_tx_div;
|
||||||
unsigned int venc_div;
|
unsigned long long venc_div;
|
||||||
|
|
||||||
if (target == MESON_VCLK_TARGET_CVBS) {
|
if (target == MESON_VCLK_TARGET_CVBS) {
|
||||||
meson_venci_cvbs_clock_config(priv);
|
meson_venci_cvbs_clock_config(priv);
|
||||||
@ -1052,27 +1058,27 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hdmi_tx_div = vclk_freq / dac_freq;
|
hdmi_tx_div = DIV_ROUND_DOWN_ULL(vclk_freq, dac_freq);
|
||||||
|
|
||||||
if (hdmi_tx_div == 0) {
|
if (hdmi_tx_div == 0) {
|
||||||
pr_err("Fatal Error, invalid HDMI-TX freq %d\n",
|
pr_err("Fatal Error, invalid HDMI-TX freq %lluHz\n",
|
||||||
dac_freq);
|
dac_freq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
venc_div = vclk_freq / venc_freq;
|
venc_div = DIV_ROUND_DOWN_ULL(vclk_freq, venc_freq);
|
||||||
|
|
||||||
if (venc_div == 0) {
|
if (venc_div == 0) {
|
||||||
pr_err("Fatal Error, invalid HDMI venc freq %d\n",
|
pr_err("Fatal Error, invalid HDMI venc freq %lluHz\n",
|
||||||
venc_freq);
|
venc_freq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (freq = 0 ; params[freq].pixel_freq ; ++freq) {
|
for (freq = 0 ; params[freq].pixel_freq ; ++freq) {
|
||||||
if ((phy_freq == params[freq].phy_freq ||
|
if ((phy_freq == params[freq].phy_freq ||
|
||||||
phy_freq == FREQ_1000_1001(params[freq].phy_freq/10)*10) &&
|
phy_freq == PHY_FREQ_1000_1001(params[freq].phy_freq)) &&
|
||||||
(vclk_freq == params[freq].vclk_freq ||
|
(vclk_freq == params[freq].vclk_freq ||
|
||||||
vclk_freq == FREQ_1000_1001(params[freq].vclk_freq))) {
|
vclk_freq == PIXEL_FREQ_1000_1001(params[freq].vclk_freq))) {
|
||||||
if (vclk_freq != params[freq].vclk_freq)
|
if (vclk_freq != params[freq].vclk_freq)
|
||||||
vic_alternate_clock = true;
|
vic_alternate_clock = true;
|
||||||
else
|
else
|
||||||
@ -1098,7 +1104,8 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!params[freq].pixel_freq) {
|
if (!params[freq].pixel_freq) {
|
||||||
pr_err("Fatal Error, invalid HDMI vclk freq %d\n", vclk_freq);
|
pr_err("Fatal Error, invalid HDMI vclk freq %lluHz\n",
|
||||||
|
vclk_freq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,17 +20,18 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* 27MHz is the CVBS Pixel Clock */
|
/* 27MHz is the CVBS Pixel Clock */
|
||||||
#define MESON_VCLK_CVBS 27000
|
#define MESON_VCLK_CVBS (27 * 1000 * 1000)
|
||||||
|
|
||||||
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 long long freq);
|
||||||
enum drm_mode_status
|
enum drm_mode_status
|
||||||
meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
|
meson_vclk_vic_supported_freq(struct meson_drm *priv,
|
||||||
unsigned int vclk_freq);
|
unsigned long long phy_freq,
|
||||||
|
unsigned long long 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 phy_freq, unsigned int vclk_freq,
|
unsigned long long phy_freq, unsigned long long vclk_freq,
|
||||||
unsigned int venc_freq, unsigned int dac_freq,
|
unsigned long long venc_freq, unsigned long long dac_freq,
|
||||||
bool hdmi_use_enci);
|
bool hdmi_use_enci);
|
||||||
|
|
||||||
#endif /* __MESON_VCLK_H */
|
#endif /* __MESON_VCLK_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user