drm/bridge: analogix_dp: Rework irq handling

Tested on RK3588.

Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
Change-Id: Iee8bbe592ea89e29b5e412727312398d3cfb3924
This commit is contained in:
Wyon Bi
2022-02-19 11:18:56 +08:00
committed by Tao Huang
parent 6502496d17
commit 1a4c9d772a
5 changed files with 121 additions and 124 deletions

View File

@@ -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);

View File

@@ -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 */

View File

@@ -14,15 +14,11 @@
#include <linux/phy/phy.h>
#include <drm/bridge/analogix_dp.h>
#include <drm/drm_probe_helper.h>
#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);
}

View File

@@ -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)
};

View File

@@ -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);