diff options
Diffstat (limited to 'drivers/gpu/drm/rcar-du/rcar_lvds.c')
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_lvds.c | 176 |
1 files changed, 101 insertions, 75 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 260ea5d8624e..ca215b588fd7 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -269,8 +269,8 @@ done: pll->pll_m, pll->pll_n, pll->pll_e, pll->div); } -static void __rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, - unsigned int freq, bool dot_clock_only) +static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, + unsigned int freq, bool dot_clock_only) { struct pll_info pll = { .diff = (unsigned long)-1 }; u32 lvdpllcr; @@ -305,52 +305,8 @@ static void __rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, rcar_lvds_write(lvds, LVDDIV, 0); } -static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) -{ - __rcar_lvds_pll_setup_d3_e3(lvds, freq, false); -} - /* ----------------------------------------------------------------------------- - * Clock - D3/E3 only - */ - -int rcar_lvds_pclk_enable(struct drm_bridge *bridge, unsigned long freq) -{ - struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); - int ret; - - if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) - return -ENODEV; - - dev_dbg(lvds->dev, "enabling LVDS PLL, freq=%luHz\n", freq); - - ret = pm_runtime_resume_and_get(lvds->dev); - if (ret) - return ret; - - __rcar_lvds_pll_setup_d3_e3(lvds, freq, true); - - return 0; -} -EXPORT_SYMBOL_GPL(rcar_lvds_pclk_enable); - -void rcar_lvds_pclk_disable(struct drm_bridge *bridge) -{ - struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); - - if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) - return; - - dev_dbg(lvds->dev, "disabling LVDS PLL\n"); - - rcar_lvds_write(lvds, LVDPLLCR, 0); - - pm_runtime_put_sync(lvds->dev); -} -EXPORT_SYMBOL_GPL(rcar_lvds_pclk_disable); - -/* ----------------------------------------------------------------------------- - * Bridge + * Enable/disable */ static enum rcar_lvds_mode rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds, @@ -394,10 +350,10 @@ static enum rcar_lvds_mode rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds, return mode; } -static void __rcar_lvds_atomic_enable(struct drm_bridge *bridge, - struct drm_atomic_state *state, - struct drm_crtc *crtc, - struct drm_connector *connector) +static void rcar_lvds_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state, + struct drm_crtc *crtc, + struct drm_connector *connector) { struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); u32 lvdhcr; @@ -410,8 +366,7 @@ static void __rcar_lvds_atomic_enable(struct drm_bridge *bridge, /* Enable the companion LVDS encoder in dual-link mode. */ if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion) - __rcar_lvds_atomic_enable(lvds->companion, state, crtc, - connector); + rcar_lvds_enable(lvds->companion, state, crtc, connector); /* * Hardcode the channels and control signals routing for now. @@ -465,8 +420,12 @@ static void __rcar_lvds_atomic_enable(struct drm_bridge *bridge, /* * PLL clock configuration on all instances but the companion in * dual-link mode. + * + * The extended PLL has been turned on by an explicit call to + * rcar_lvds_pclk_enable() from the DU driver. */ - if (lvds->link_type == RCAR_LVDS_SINGLE_LINK || lvds->companion) { + if ((lvds->link_type == RCAR_LVDS_SINGLE_LINK || lvds->companion) && + !(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { const struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); const struct drm_display_mode *mode = @@ -531,22 +490,7 @@ static void __rcar_lvds_atomic_enable(struct drm_bridge *bridge, rcar_lvds_write(lvds, LVDCR0, lvdcr0); } -static void rcar_lvds_atomic_enable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) -{ - struct drm_atomic_state *state = old_bridge_state->base.state; - struct drm_connector *connector; - struct drm_crtc *crtc; - - connector = drm_atomic_get_new_connector_for_encoder(state, - bridge->encoder); - crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; - - __rcar_lvds_atomic_enable(bridge, state, crtc, connector); -} - -static void rcar_lvds_atomic_disable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) +static void rcar_lvds_disable(struct drm_bridge *bridge) { struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); u32 lvdcr0; @@ -578,15 +522,99 @@ static void rcar_lvds_atomic_disable(struct drm_bridge *bridge, rcar_lvds_write(lvds, LVDCR0, 0); rcar_lvds_write(lvds, LVDCR1, 0); - rcar_lvds_write(lvds, LVDPLLCR, 0); + + /* The extended PLL is turned off in rcar_lvds_pclk_disable(). */ + if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) + rcar_lvds_write(lvds, LVDPLLCR, 0); /* Disable the companion LVDS encoder in dual-link mode. */ if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion) - lvds->companion->funcs->atomic_disable(lvds->companion, - old_bridge_state); + rcar_lvds_disable(lvds->companion); + + pm_runtime_put_sync(lvds->dev); +} + +/* ----------------------------------------------------------------------------- + * Clock - D3/E3 only + */ + +int rcar_lvds_pclk_enable(struct drm_bridge *bridge, unsigned long freq, + bool dot_clk_only) +{ + struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); + int ret; + + if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) + return -ENODEV; + + dev_dbg(lvds->dev, "enabling LVDS PLL, freq=%luHz\n", freq); + + ret = pm_runtime_resume_and_get(lvds->dev); + if (ret) + return ret; + + rcar_lvds_pll_setup_d3_e3(lvds, freq, dot_clk_only); + + return 0; +} +EXPORT_SYMBOL_GPL(rcar_lvds_pclk_enable); + +void rcar_lvds_pclk_disable(struct drm_bridge *bridge, bool dot_clk_only) +{ + struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); + + if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) + return; + + dev_dbg(lvds->dev, "disabling LVDS PLL\n"); + + if (!dot_clk_only) + rcar_lvds_disable(bridge); + + rcar_lvds_write(lvds, LVDPLLCR, 0); pm_runtime_put_sync(lvds->dev); } +EXPORT_SYMBOL_GPL(rcar_lvds_pclk_disable); + +/* ----------------------------------------------------------------------------- + * Bridge + */ + +static void rcar_lvds_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct drm_atomic_state *state = old_bridge_state->base.state; + struct drm_connector *connector; + struct drm_crtc *crtc; + + connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; + + rcar_lvds_enable(bridge, state, crtc, connector); +} + +static void rcar_lvds_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); + + /* + * For D3 and E3, disabling the LVDS encoder before the DU would stall + * the DU, causing a vblank wait timeout when stopping the DU. This has + * been traced to clearing the LVEN bit, but the exact reason is + * unknown. Keep the encoder enabled, it will be disabled by an explicit + * call to rcar_lvds_pclk_disable() from the DU driver. + * + * We could clear the LVRES bit already to disable the LVDS output, but + * that's likely pointless. + */ + if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL) + return; + + rcar_lvds_disable(bridge); +} static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, @@ -922,14 +950,12 @@ static const struct rcar_lvds_device_info rcar_lvds_r8a77990_info = { .gen = 3, .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_EXT_PLL | RCAR_LVDS_QUIRK_DUAL_LINK, - .pll_setup = rcar_lvds_pll_setup_d3_e3, }; static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = { .gen = 3, .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_PWD | RCAR_LVDS_QUIRK_EXT_PLL | RCAR_LVDS_QUIRK_DUAL_LINK, - .pll_setup = rcar_lvds_pll_setup_d3_e3, }; static const struct of_device_id rcar_lvds_of_table[] = { |