From 1a4c9d772a998f14e3cfc5c49a90c7bfc3c6a2d6 Mon Sep 17 00:00:00 2001 From: Wyon Bi Date: Sat, 19 Feb 2022 11:18:56 +0800 Subject: [PATCH] drm/bridge: analogix_dp: Rework irq handling Tested on RK3588. Signed-off-by: Wyon Bi Change-Id: Iee8bbe592ea89e29b5e412727312398d3cfb3924 --- .../drm/bridge/analogix/analogix_dp_core.c | 111 ++++++++---------- .../drm/bridge/analogix/analogix_dp_core.h | 13 +- .../gpu/drm/bridge/analogix/analogix_dp_reg.c | 98 ++++++++-------- .../gpu/drm/rockchip/analogix_dp-rockchip.c | 21 ++++ include/drm/bridge/analogix_dp.h | 2 + 5 files changed, 121 insertions(+), 124 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 6740d0f67e21..99deba0d266c 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -920,49 +920,11 @@ static irqreturn_t analogix_dp_hpd_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -static irqreturn_t analogix_dp_hardirq(int irq, void *arg) -{ - struct analogix_dp_device *dp = arg; - enum dp_irq_type irq_type; - int ret; - - ret = pm_runtime_get_sync(dp->dev); - if (ret < 0) - return IRQ_NONE; - - irq_type = analogix_dp_get_irq_type(dp); - if (irq_type != DP_IRQ_TYPE_UNKNOWN) - analogix_dp_mute_hpd_interrupt(dp); - - pm_runtime_put_sync(dp->dev); - - return IRQ_WAKE_THREAD; -} - static irqreturn_t analogix_dp_irq_thread(int irq, void *arg) { struct analogix_dp_device *dp = arg; - enum dp_irq_type irq_type; - int ret; - ret = pm_runtime_get_sync(dp->dev); - if (ret < 0) - return IRQ_NONE; - - irq_type = analogix_dp_get_irq_type(dp); - if (irq_type & DP_IRQ_TYPE_HP_CABLE_IN || - irq_type & DP_IRQ_TYPE_HP_CABLE_OUT) { - dev_dbg(dp->dev, "Detected cable status changed!\n"); - if (dp->drm_dev) - drm_helper_hpd_irq_event(dp->drm_dev); - } - - if (irq_type != DP_IRQ_TYPE_UNKNOWN) { - analogix_dp_clear_hotplug_interrupts(dp); - analogix_dp_unmute_hpd_interrupt(dp); - } - - pm_runtime_put_sync(dp->dev); + analogix_dp_irq_handler(dp); return IRQ_HANDLED; } @@ -1173,24 +1135,28 @@ static int analogix_dp_get_modes(struct drm_connector *connector) { struct analogix_dp_device *dp = to_dp(connector); struct edid *edid; - int num_modes = 0; + int ret, num_modes = 0; if (dp->plat_data->panel) num_modes += drm_panel_get_modes(dp->plat_data->panel, connector); if (!num_modes) { + ret = analogix_dp_phy_power_on(dp); + if (ret) + return 0; + if (dp->plat_data->panel) analogix_dp_panel_prepare(dp); - pm_runtime_get_sync(dp->dev); edid = drm_get_edid(connector, &dp->aux.ddc); - pm_runtime_put(dp->dev); if (edid) { drm_connector_update_edid_property(&dp->connector, edid); num_modes += drm_add_edid_modes(&dp->connector, edid); kfree(edid); } + + analogix_dp_phy_power_off(dp); } if (dp->plat_data->get_modes) @@ -1253,11 +1219,13 @@ analogix_dp_detect(struct analogix_dp_device *dp) enum drm_connector_status status = connector_status_disconnected; int ret; + ret = analogix_dp_phy_power_on(dp); + if (ret) + return connector_status_disconnected; + if (dp->plat_data->panel) analogix_dp_panel_prepare(dp); - pm_runtime_get_sync(dp->dev); - if (!analogix_dp_detect_hpd(dp)) { ret = analogix_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate); if (ret) { @@ -1275,7 +1243,7 @@ analogix_dp_detect(struct analogix_dp_device *dp) } out: - pm_runtime_put(dp->dev); + analogix_dp_phy_power_off(dp); return status; } @@ -1404,12 +1372,12 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp) { int ret; - pm_runtime_get_sync(dp->dev); - if (dp->plat_data->power_on_start) dp->plat_data->power_on_start(dp->plat_data); - analogix_dp_phy_power_on(dp); + ret = analogix_dp_phy_power_on(dp); + if (ret) + return ret; ret = analogix_dp_init_dp(dp); if (ret) @@ -1438,15 +1406,12 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp) if (dp->plat_data->power_on_end) dp->plat_data->power_on_end(dp->plat_data); - enable_irq(dp->irq); return 0; out_dp_init: analogix_dp_phy_power_off(dp); if (dp->plat_data->power_off) dp->plat_data->power_off(dp->plat_data); - pm_runtime_put_sync(dp->dev); - return ret; } @@ -1519,17 +1484,12 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge) if (!analogix_dp_get_plug_in_status(dp)) analogix_dp_link_power_down(dp); - disable_irq(dp->irq); - if (dp->plat_data->power_off) dp->plat_data->power_off(dp->plat_data); - analogix_dp_reset_aux(dp); analogix_dp_set_analog_power_down(dp, POWER_ALL, 1); analogix_dp_phy_power_off(dp); - pm_runtime_put_sync(dp->dev); - if (dp->plat_data->panel) analogix_dp_panel_unprepare(dp); @@ -1839,13 +1799,9 @@ int analogix_dp_loader_protect(struct analogix_dp_device *dp) { int ret; - ret = pm_runtime_resume_and_get(dp->dev); - if (ret) { - dev_err(dp->dev, "failed to get runtime PM: %d\n", ret); + ret = analogix_dp_phy_power_on(dp); + if (ret) return ret; - } - - analogix_dp_phy_power_on(dp); dp->dpms_mode = DRM_MODE_DPMS_ON; @@ -1965,10 +1921,9 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data) } irq_set_status_flags(dp->irq, IRQ_NOAUTOEN); - ret = devm_request_threaded_irq(&pdev->dev, dp->irq, - analogix_dp_hardirq, + ret = devm_request_threaded_irq(dev, dp->irq, NULL, analogix_dp_irq_thread, - 0, "analogix-dp", dp); + IRQF_ONESHOT, dev_name(dev), dp); if (ret) { dev_err(&pdev->dev, "failed to request irq\n"); return ERR_PTR(ret); @@ -1997,6 +1952,8 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev) return ret; pm_runtime_enable(dp->dev); + pm_runtime_get_sync(dp->dev); + analogix_dp_init(dp); ret = analogix_dp_bridge_init(dp); if (ret) { @@ -2004,9 +1961,12 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev) goto err_disable_pm_runtime; } + enable_irq(dp->irq); + return 0; err_disable_pm_runtime: + pm_runtime_put(dp->dev); pm_runtime_disable(dp->dev); return ret; @@ -2015,8 +1975,10 @@ EXPORT_SYMBOL_GPL(analogix_dp_bind); void analogix_dp_unbind(struct analogix_dp_device *dp) { + disable_irq(dp->irq); dp->connector.funcs->destroy(&dp->connector); drm_dp_aux_unregister(&dp->aux); + pm_runtime_put(dp->dev); pm_runtime_disable(dp->dev); } EXPORT_SYMBOL_GPL(analogix_dp_unbind); @@ -2027,6 +1989,25 @@ void analogix_dp_remove(struct analogix_dp_device *dp) } EXPORT_SYMBOL_GPL(analogix_dp_remove); +int analogix_dp_suspend(struct analogix_dp_device *dp) +{ + disable_irq(dp->irq); + pm_runtime_force_suspend(dp->dev); + + return 0; +} +EXPORT_SYMBOL_GPL(analogix_dp_suspend); + +int analogix_dp_resume(struct analogix_dp_device *dp) +{ + pm_runtime_force_resume(dp->dev); + analogix_dp_init(dp); + enable_irq(dp->irq); + + return 0; +} +EXPORT_SYMBOL_GPL(analogix_dp_resume); + int analogix_dp_runtime_suspend(struct analogix_dp_device *dp) { clk_bulk_disable_unprepare(dp->nr_clks, dp->clks); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index 322672b63dde..059b1a229ad3 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -122,13 +122,6 @@ enum analog_power_block { POWER_ALL }; -enum dp_irq_type { - DP_IRQ_TYPE_HP_CABLE_IN = BIT(0), - DP_IRQ_TYPE_HP_CABLE_OUT = BIT(1), - DP_IRQ_TYPE_HP_CHANGE = BIT(2), - DP_IRQ_TYPE_UNKNOWN = BIT(3), -}; - struct video_info { char *name; struct drm_display_mode mode; @@ -176,7 +169,6 @@ struct analogix_dp_device { struct video_info video_info; struct link_train link_train; struct phy *phy; - bool phy_enabled; int dpms_mode; struct gpio_desc *hpd_gpiod; bool force_hpd; @@ -210,7 +202,6 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, int analogix_dp_init_analog_func(struct analogix_dp_device *dp); void analogix_dp_init_hpd(struct analogix_dp_device *dp); void analogix_dp_force_hpd(struct analogix_dp_device *dp); -enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp); void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp); void analogix_dp_reset_aux(struct analogix_dp_device *dp); void analogix_dp_init_aux(struct analogix_dp_device *dp); @@ -252,11 +243,13 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp, void analogix_dp_set_video_format(struct analogix_dp_device *dp); void analogix_dp_video_bist_enable(struct analogix_dp_device *dp); bool analogix_dp_ssc_supported(struct analogix_dp_device *dp); -void analogix_dp_phy_power_on(struct analogix_dp_device *dp); +int analogix_dp_phy_power_on(struct analogix_dp_device *dp); void analogix_dp_phy_power_off(struct analogix_dp_device *dp); void analogix_dp_audio_config_spdif(struct analogix_dp_device *dp); void analogix_dp_audio_config_i2s(struct analogix_dp_device *dp); void analogix_dp_audio_enable(struct analogix_dp_device *dp); void analogix_dp_audio_disable(struct analogix_dp_device *dp); +void analogix_dp_init(struct analogix_dp_device *dp); +void analogix_dp_irq_handler(struct analogix_dp_device *dp); #endif /* _ANALOGIX_DP_CORE_H */ diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index 863cce70e374..e2ac2d720b36 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -14,15 +14,11 @@ #include #include +#include #include "analogix_dp_core.h" #include "analogix_dp_reg.h" -#define COMMON_INT_MASK_1 0 -#define COMMON_INT_MASK_2 0 -#define COMMON_INT_MASK_3 0 -#define COMMON_INT_MASK_4 (HOTPLUG_CHG | HPD_LOST | PLUG) - static void analogix_dp_write(struct analogix_dp_device *dp, u32 reg, u32 val) { if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { @@ -168,9 +164,6 @@ void analogix_dp_reset(struct analogix_dp_device *dp) analogix_dp_write(dp, ANALOGIX_DP_PKT_SEND_CTL, 0x0); analogix_dp_write(dp, ANALOGIX_DP_HDCP_CTL, 0x0); - analogix_dp_write(dp, ANALOGIX_DP_HPD_DEGLITCH_L, 0x5e); - analogix_dp_write(dp, ANALOGIX_DP_HPD_DEGLITCH_H, 0x1a); - analogix_dp_write(dp, ANALOGIX_DP_LINK_DEBUG_CTL, 0x10); analogix_dp_write(dp, ANALOGIX_DP_PHY_TEST, 0x0); @@ -191,17 +184,10 @@ void analogix_dp_swreset(struct analogix_dp_device *dp) void analogix_dp_config_interrupt(struct analogix_dp_device *dp) { - u32 reg; - /* 0: mask, 1: unmask */ - reg = COMMON_INT_MASK_1; - analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_1, reg); - - reg = COMMON_INT_MASK_2; - analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_2, reg); - - reg = COMMON_INT_MASK_3; - analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_3, reg); + analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_1, 0); + analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_2, 0); + analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_3, 0); if (dp->force_hpd || dp->hpd_gpiod) analogix_dp_mute_hpd_interrupt(dp); @@ -215,7 +201,7 @@ void analogix_dp_mute_hpd_interrupt(struct analogix_dp_device *dp) /* 0: mask, 1: unmask */ reg = analogix_dp_read(dp, ANALOGIX_DP_COMMON_INT_MASK_4); - reg &= ~COMMON_INT_MASK_4; + reg &= ~HOTPLUG_CHG; analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_4, reg); reg = analogix_dp_read(dp, ANALOGIX_DP_INT_STA_MASK); @@ -228,7 +214,8 @@ void analogix_dp_unmute_hpd_interrupt(struct analogix_dp_device *dp) u32 reg; /* 0: mask, 1: unmask */ - reg = COMMON_INT_MASK_4; + reg = analogix_dp_read(dp, ANALOGIX_DP_COMMON_INT_MASK_4); + reg |= HOTPLUG_CHG; analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_4, reg); reg = analogix_dp_read(dp, ANALOGIX_DP_INT_STA_MASK); @@ -419,7 +406,8 @@ void analogix_dp_init_hpd(struct analogix_dp_device *dp) if (dp->hpd_gpiod) return; - analogix_dp_clear_hotplug_interrupts(dp); + analogix_dp_write(dp, ANALOGIX_DP_HPD_DEGLITCH_H, 0xbb); + analogix_dp_write(dp, ANALOGIX_DP_HPD_DEGLITCH_L, 0x80); reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_3); reg &= ~(F_HPD | HPD_CTRL); @@ -435,23 +423,30 @@ void analogix_dp_force_hpd(struct analogix_dp_device *dp) analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_3, reg); } -enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp) +static void analogix_dp_handle_hpd_event(struct analogix_dp_device *dp) { + bool changed = false; u32 reg; - /* Parse hotplug interrupt status register */ + reg = analogix_dp_read(dp, ANALOGIX_DP_INT_STA); + if (reg & INT_HPD) { + dev_info(dp->dev, "irq-hpd, it's being ignored for now\n"); + analogix_dp_write(dp, ANALOGIX_DP_INT_STA, INT_HPD); + } + reg = analogix_dp_read(dp, ANALOGIX_DP_COMMON_INT_STA_4); + if (reg & HOTPLUG_CHG) { + analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_STA_4, HOTPLUG_CHG); + changed = true; + } - if (reg & PLUG) - return DP_IRQ_TYPE_HP_CABLE_IN; + if (changed) + drm_helper_hpd_irq_event(dp->drm_dev); +} - if (reg & HPD_LOST) - return DP_IRQ_TYPE_HP_CABLE_OUT; - - if (reg & HOTPLUG_CHG) - return DP_IRQ_TYPE_HP_CHANGE; - - return DP_IRQ_TYPE_UNKNOWN; +void analogix_dp_irq_handler(struct analogix_dp_device *dp) +{ + analogix_dp_handle_hpd_event(dp); } void analogix_dp_reset_aux(struct analogix_dp_device *dp) @@ -1080,25 +1075,28 @@ int analogix_dp_send_psr_spd(struct analogix_dp_device *dp, return 0; } -void analogix_dp_phy_power_on(struct analogix_dp_device *dp) +int analogix_dp_phy_power_on(struct analogix_dp_device *dp) { - if (dp->phy_enabled) - return; + int ret; - phy_set_mode(dp->phy, PHY_MODE_DP); - phy_power_on(dp->phy); + ret = phy_set_mode(dp->phy, PHY_MODE_DP); + if (ret) { + dev_err(dp->dev, "phy_set_mode failed: %d\n", ret); + return ret; + } - dp->phy_enabled = true; + ret = phy_power_on(dp->phy); + if (ret) { + dev_err(dp->dev, "phy_power_on failed: %d\n", ret); + return ret; + } + + return ret; } void analogix_dp_phy_power_off(struct analogix_dp_device *dp) { - if (!dp->phy_enabled) - return; - phy_power_off(dp->phy); - - dp->phy_enabled = false; } enum { @@ -1126,12 +1124,6 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp, if (WARN_ON(msg->size > 16)) return -E2BIG; - reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_2); - if (reg & AUX_FUNC_EN_N) { - analogix_dp_phy_power_on(dp); - analogix_dp_init_aux(dp); - } - /* Clear AUX CH data buffer */ reg = BUF_CLR; analogix_dp_write(dp, ANALOGIX_DP_BUFFER_DATA_CTL, reg); @@ -1367,3 +1359,11 @@ void analogix_dp_audio_disable(struct analogix_dp_device *dp) reg |= AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N; analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_1, reg); } + +void analogix_dp_init(struct analogix_dp_device *dp) +{ + analogix_dp_init_interrupt(dp); + analogix_dp_config_interrupt(dp); + analogix_dp_init_hpd(dp); + analogix_dp_init_aux(dp); +} diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 4726b16a36ec..34adcc73238e 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -618,6 +618,26 @@ static int rockchip_dp_remove(struct platform_device *pdev) return 0; } +static __maybe_unused int rockchip_dp_suspend(struct device *dev) +{ + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + if (IS_ERR(dp->adp)) + return 0; + + return analogix_dp_suspend(dp->adp); +} + +static __maybe_unused int rockchip_dp_resume(struct device *dev) +{ + struct rockchip_dp_device *dp = dev_get_drvdata(dev); + + if (IS_ERR(dp->adp)) + return 0; + + return analogix_dp_resume(dp->adp); +} + static __maybe_unused int rockchip_dp_runtime_suspend(struct device *dev) { struct rockchip_dp_device *dp = dev_get_drvdata(dev); @@ -639,6 +659,7 @@ static __maybe_unused int rockchip_dp_runtime_resume(struct device *dev) } static const struct dev_pm_ops rockchip_dp_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rockchip_dp_suspend, rockchip_dp_resume) SET_RUNTIME_PM_OPS(rockchip_dp_runtime_suspend, rockchip_dp_runtime_resume, NULL) }; diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index 26b4246cbe6b..121fde705e9f 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -57,6 +57,8 @@ struct analogix_dp_plat_data { void (*convert_to_origin_mode)(struct drm_display_mode *); }; +int analogix_dp_resume(struct analogix_dp_device *dp); +int analogix_dp_suspend(struct analogix_dp_device *dp); int analogix_dp_runtime_resume(struct analogix_dp_device *dp); int analogix_dp_runtime_suspend(struct analogix_dp_device *dp);