Commit c64c8e04 authored by Jernej Skrabec's avatar Jernej Skrabec Committed by Maarten Lankhorst

drm/sun4i: dw-hdmi: Fix HDMI PHY clock setup

Recent rework, which made HDMI PHY driver a platform device, inadvertely
reversed clock setup order. HW is very touchy about it. Proper way is to
handle controllers resets and clocks first and HDMI PHYs second.

Currently, without this fix, first mode set completely fails (nothing on
HDMI monitor) on H3 era PHYs. On H6, it still somehow work.

Move HDMI PHY reset & clocks handling to sun8i_hdmi_phy_init() which
will assure that code is executed after controllers reset & clocks are
handled. Additionally, add sun8i_hdmi_phy_deinit() which will deinit
them at controllers driver unload.

Tested on A64, H3, H6 and R40.

Fixes: 9bf37977 ("drm/sun4i: dw-hdmi: Make HDMI PHY into a platform device")
Signed-off-by: default avatarJernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: default avatarMaxime Ripard <maxime@cerno.tech>
Link: https://patchwork.freedesktop.org/patch/msgid/20210915175836.3158839-1-jernej.skrabec@gmail.comSigned-off-by: default avatarMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
parent 5e2e412d
...@@ -216,11 +216,13 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, ...@@ -216,11 +216,13 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master,
goto err_disable_clk_tmds; goto err_disable_clk_tmds;
} }
ret = sun8i_hdmi_phy_init(hdmi->phy);
if (ret)
goto err_disable_clk_tmds;
drm_encoder_helper_add(encoder, &sun8i_dw_hdmi_encoder_helper_funcs); drm_encoder_helper_add(encoder, &sun8i_dw_hdmi_encoder_helper_funcs);
drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
sun8i_hdmi_phy_init(hdmi->phy);
plat_data->mode_valid = hdmi->quirks->mode_valid; plat_data->mode_valid = hdmi->quirks->mode_valid;
plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe; plat_data->use_drm_infoframe = hdmi->quirks->use_drm_infoframe;
sun8i_hdmi_phy_set_ops(hdmi->phy, plat_data); sun8i_hdmi_phy_set_ops(hdmi->phy, plat_data);
...@@ -262,6 +264,7 @@ static void sun8i_dw_hdmi_unbind(struct device *dev, struct device *master, ...@@ -262,6 +264,7 @@ static void sun8i_dw_hdmi_unbind(struct device *dev, struct device *master,
struct sun8i_dw_hdmi *hdmi = dev_get_drvdata(dev); struct sun8i_dw_hdmi *hdmi = dev_get_drvdata(dev);
dw_hdmi_unbind(hdmi->hdmi); dw_hdmi_unbind(hdmi->hdmi);
sun8i_hdmi_phy_deinit(hdmi->phy);
clk_disable_unprepare(hdmi->clk_tmds); clk_disable_unprepare(hdmi->clk_tmds);
reset_control_assert(hdmi->rst_ctrl); reset_control_assert(hdmi->rst_ctrl);
gpiod_set_value(hdmi->ddc_en, 0); gpiod_set_value(hdmi->ddc_en, 0);
......
...@@ -169,6 +169,7 @@ struct sun8i_hdmi_phy { ...@@ -169,6 +169,7 @@ struct sun8i_hdmi_phy {
struct clk *clk_phy; struct clk *clk_phy;
struct clk *clk_pll0; struct clk *clk_pll0;
struct clk *clk_pll1; struct clk *clk_pll1;
struct device *dev;
unsigned int rcal; unsigned int rcal;
struct regmap *regs; struct regmap *regs;
struct reset_control *rst_phy; struct reset_control *rst_phy;
...@@ -205,7 +206,8 @@ encoder_to_sun8i_dw_hdmi(struct drm_encoder *encoder) ...@@ -205,7 +206,8 @@ encoder_to_sun8i_dw_hdmi(struct drm_encoder *encoder)
int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node); int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node);
void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy); int sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy);
void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy);
void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy, void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
struct dw_hdmi_plat_data *plat_data); struct dw_hdmi_plat_data *plat_data);
......
...@@ -506,9 +506,60 @@ static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy) ...@@ -506,9 +506,60 @@ static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy)
phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2; phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2;
} }
void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy) int sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy)
{ {
int ret;
ret = reset_control_deassert(phy->rst_phy);
if (ret) {
dev_err(phy->dev, "Cannot deassert phy reset control: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(phy->clk_bus);
if (ret) {
dev_err(phy->dev, "Cannot enable bus clock: %d\n", ret);
goto err_assert_rst_phy;
}
ret = clk_prepare_enable(phy->clk_mod);
if (ret) {
dev_err(phy->dev, "Cannot enable mod clock: %d\n", ret);
goto err_disable_clk_bus;
}
if (phy->variant->has_phy_clk) {
ret = sun8i_phy_clk_create(phy, phy->dev,
phy->variant->has_second_pll);
if (ret) {
dev_err(phy->dev, "Couldn't create the PHY clock\n");
goto err_disable_clk_mod;
}
clk_prepare_enable(phy->clk_phy);
}
phy->variant->phy_init(phy); phy->variant->phy_init(phy);
return 0;
err_disable_clk_mod:
clk_disable_unprepare(phy->clk_mod);
err_disable_clk_bus:
clk_disable_unprepare(phy->clk_bus);
err_assert_rst_phy:
reset_control_assert(phy->rst_phy);
return ret;
}
void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
{
clk_disable_unprepare(phy->clk_mod);
clk_disable_unprepare(phy->clk_bus);
clk_disable_unprepare(phy->clk_phy);
reset_control_assert(phy->rst_phy);
} }
void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy, void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
...@@ -638,6 +689,7 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev) ...@@ -638,6 +689,7 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
phy->variant = (struct sun8i_hdmi_phy_variant *)match->data; phy->variant = (struct sun8i_hdmi_phy_variant *)match->data;
phy->dev = dev;
ret = of_address_to_resource(node, 0, &res); ret = of_address_to_resource(node, 0, &res);
if (ret) { if (ret) {
...@@ -696,47 +748,10 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev) ...@@ -696,47 +748,10 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
goto err_put_clk_pll1; goto err_put_clk_pll1;
} }
ret = reset_control_deassert(phy->rst_phy);
if (ret) {
dev_err(dev, "Cannot deassert phy reset control: %d\n", ret);
goto err_put_rst_phy;
}
ret = clk_prepare_enable(phy->clk_bus);
if (ret) {
dev_err(dev, "Cannot enable bus clock: %d\n", ret);
goto err_deassert_rst_phy;
}
ret = clk_prepare_enable(phy->clk_mod);
if (ret) {
dev_err(dev, "Cannot enable mod clock: %d\n", ret);
goto err_disable_clk_bus;
}
if (phy->variant->has_phy_clk) {
ret = sun8i_phy_clk_create(phy, dev,
phy->variant->has_second_pll);
if (ret) {
dev_err(dev, "Couldn't create the PHY clock\n");
goto err_disable_clk_mod;
}
clk_prepare_enable(phy->clk_phy);
}
platform_set_drvdata(pdev, phy); platform_set_drvdata(pdev, phy);
return 0; return 0;
err_disable_clk_mod:
clk_disable_unprepare(phy->clk_mod);
err_disable_clk_bus:
clk_disable_unprepare(phy->clk_bus);
err_deassert_rst_phy:
reset_control_assert(phy->rst_phy);
err_put_rst_phy:
reset_control_put(phy->rst_phy);
err_put_clk_pll1: err_put_clk_pll1:
clk_put(phy->clk_pll1); clk_put(phy->clk_pll1);
err_put_clk_pll0: err_put_clk_pll0:
...@@ -753,12 +768,6 @@ static int sun8i_hdmi_phy_remove(struct platform_device *pdev) ...@@ -753,12 +768,6 @@ static int sun8i_hdmi_phy_remove(struct platform_device *pdev)
{ {
struct sun8i_hdmi_phy *phy = platform_get_drvdata(pdev); struct sun8i_hdmi_phy *phy = platform_get_drvdata(pdev);
clk_disable_unprepare(phy->clk_mod);
clk_disable_unprepare(phy->clk_bus);
clk_disable_unprepare(phy->clk_phy);
reset_control_assert(phy->rst_phy);
reset_control_put(phy->rst_phy); reset_control_put(phy->rst_phy);
clk_put(phy->clk_pll0); clk_put(phy->clk_pll0);
......
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