drm/msm/dp: Add Display Port HPD feature

Configure HPD registers in DP controller and
enable HPD interrupt.

Add interrupt to handle HPD connect and disconnect events.

Changes in v8: None

Signed-off-by: Tanmay Shah <tanmay@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@chromium.org>
This commit is contained in:
Tanmay Shah 2020-08-27 14:16:58 -07:00 committed by Rob Clark
parent a10476e450
commit 220b856a3d
7 changed files with 180 additions and 33 deletions

View File

@ -774,6 +774,23 @@ static void dpu_irq_preinstall(struct msm_kms *kms)
dpu_core_irq_preinstall(dpu_kms);
}
static int dpu_irq_postinstall(struct msm_kms *kms)
{
struct msm_drm_private *priv;
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
if (!dpu_kms || !dpu_kms->dev)
return -EINVAL;
priv = dpu_kms->dev->dev_private;
if (!priv)
return -EINVAL;
msm_dp_irq_postinstall(priv->dp);
return 0;
}
static void dpu_irq_uninstall(struct msm_kms *kms)
{
struct dpu_kms *dpu_kms = to_dpu_kms(kms);
@ -784,6 +801,7 @@ static void dpu_irq_uninstall(struct msm_kms *kms)
static const struct msm_kms_funcs kms_funcs = {
.hw_init = dpu_kms_hw_init,
.irq_preinstall = dpu_irq_preinstall,
.irq_postinstall = dpu_irq_postinstall,
.irq_uninstall = dpu_irq_uninstall,
.irq = dpu_irq,
.enable_commit = dpu_kms_enable_commit,

View File

@ -17,7 +17,6 @@
#define POLLING_SLEEP_US 1000
#define POLLING_TIMEOUT_US 10000
#define REFTIMER_DEFAULT_VALUE 0x20000
#define SCRAMBLER_RESET_COUNT_VALUE 0xFC
#define DP_INTERRUPT_STATUS_ACK_SHIFT 1
@ -746,35 +745,51 @@ void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog,
}
}
void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog, bool en)
void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog,
u32 intr_mask, bool en)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
u32 config = dp_read_aux(catalog, REG_DP_DP_HPD_INT_MASK);
config = (en ? config | intr_mask : config & ~intr_mask);
dp_write_aux(catalog, REG_DP_DP_HPD_INT_MASK,
config & DP_DP_HPD_INT_MASK);
}
void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
if (en) {
u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);
dp_write_aux(catalog, REG_DP_DP_HPD_INT_ACK,
DP_DP_HPD_PLUG_INT_ACK |
DP_DP_IRQ_HPD_INT_ACK |
DP_DP_HPD_REPLUG_INT_ACK |
DP_DP_HPD_UNPLUG_INT_ACK);
dp_write_aux(catalog, REG_DP_DP_HPD_INT_MASK,
DP_DP_HPD_PLUG_INT_MASK |
DP_DP_IRQ_HPD_INT_MASK |
DP_DP_HPD_REPLUG_INT_MASK |
DP_DP_HPD_UNPLUG_INT_MASK);
/* enable HPD interrupts */
dp_catalog_hpd_config_intr(dp_catalog,
DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK
| DP_DP_HPD_UNPLUG_INT_MASK, true);
/* Configure REFTIMER */
reftimer |= REFTIMER_DEFAULT_VALUE;
/* Configure REFTIMER and enable it */
reftimer |= DP_DP_HPD_REFTIMER_ENABLE;
dp_write_aux(catalog, REG_DP_DP_HPD_REFTIMER, reftimer);
/* Enable HPD */
dp_write_aux(catalog, REG_DP_DP_HPD_CTRL,
DP_DP_HPD_CTRL_HPD_EN);
} else {
/* Disable HPD */
dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, 0x0);
dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, DP_DP_HPD_CTRL_HPD_EN);
}
u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
int isr = 0;
isr = dp_read_aux(catalog, REG_DP_DP_HPD_INT_STATUS);
dp_write_aux(catalog, REG_DP_DP_HPD_INT_ACK,
(isr & DP_DP_HPD_INT_MASK));
return isr;
}
int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog)

View File

@ -76,7 +76,10 @@ void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_usb_reset(struct dp_catalog *dp_catalog, bool flip);
bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable);
void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog, bool enable);
void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog,
u32 intr_mask, bool en);
void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog);
u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog);
void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog *dp_catalog, bool flipped,
u8 lane_cnt);

View File

@ -1563,7 +1563,6 @@ int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
rate = ctrl->panel->link_info.rate;
dp_power_clk_enable(ctrl->power, DP_CORE_PM, true);
dp_catalog_ctrl_hpd_config(ctrl->catalog, true);
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
DRM_DEBUG_DP("using phy test link parameters\n");

View File

@ -17,6 +17,7 @@
#include "dp_power.h"
#include "dp_catalog.h"
#include "dp_aux.h"
#include "dp_reg.h"
#include "dp_link.h"
#include "dp_panel.h"
#include "dp_ctrl.h"
@ -36,6 +37,7 @@ struct dp_display_private {
bool power_on;
bool hpd_irq_on;
bool audio_supported;
atomic_t hpd_isr_status;
struct platform_device *pdev;
struct dentry *root;
@ -54,6 +56,8 @@ struct dp_display_private {
struct dp_usbpd_cb usbpd_cb;
struct dp_display_mode dp_mode;
struct msm_dp dp_display;
struct delayed_work config_hpd_work;
};
static const struct of_device_id dp_dt_match[] = {
@ -64,6 +68,20 @@ static const struct of_device_id dp_dt_match[] = {
static irqreturn_t dp_display_irq(int irq, void *dev_id)
{
struct dp_display_private *dp = dev_id;
irqreturn_t ret = IRQ_HANDLED;
u32 hpd_isr_status;
if (!dp) {
DRM_ERROR("invalid data\n");
return IRQ_NONE;
}
hpd_isr_status = dp_catalog_hpd_get_intr_status(dp->catalog);
if (hpd_isr_status & DP_DP_HPD_INT_MASK) {
atomic_set(&dp->hpd_isr_status, hpd_isr_status);
ret = IRQ_WAKE_THREAD;
}
/* DP controller isr */
dp_ctrl_isr(dp->ctrl);
@ -71,6 +89,54 @@ static irqreturn_t dp_display_irq(int irq, void *dev_id)
/* DP aux isr */
dp_aux_isr(dp->aux);
return ret;
}
static irqreturn_t dp_display_hpd_isr_work(int irq, void *data)
{
struct dp_display_private *dp;
struct dp_usbpd *hpd;
u32 isr = 0;
dp = (struct dp_display_private *)data;
if (!dp)
return IRQ_NONE;
isr = atomic_read(&dp->hpd_isr_status);
/* reset to default */
atomic_set(&dp->hpd_isr_status, 0);
hpd = dp->usbpd;
if (!hpd)
return IRQ_NONE;
if (isr & DP_DP_HPD_PLUG_INT_MASK &&
isr & DP_DP_HPD_STATE_STATUS_CONNECTED) {
hpd->hpd_high = 1;
dp->usbpd_cb.configure(&dp->pdev->dev);
} else if (isr & DP_DP_HPD_UNPLUG_INT_MASK &&
(isr & DP_DP_HPD_STATE_STATUS_MASK) ==
DP_DP_HPD_STATE_STATUS_DISCONNECTED) {
/* disable HPD plug interrupt until disconnect is done
*/
dp_catalog_hpd_config_intr(dp->catalog,
DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK,
false);
hpd->hpd_high = 0;
/* We don't need separate work for disconnect as
* connect/attention interrupts are disabled
*/
dp->usbpd_cb.disconnect(&dp->pdev->dev);
dp_catalog_hpd_config_intr(dp->catalog,
DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK,
true);
}
return IRQ_HANDLED;
}
@ -212,8 +278,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
int rc = 0;
struct edid *edid;
dp_aux_init(dp->aux);
if (dp->link->psm_enabled)
goto notify;
@ -270,10 +334,6 @@ static void dp_display_host_deinit(struct dp_display_private *dp)
return;
}
dp_ctrl_host_deinit(dp->ctrl);
dp_aux_deinit(dp->aux);
dp_power_deinit(dp->power);
disable_irq(dp->irq);
dp->core_initialized = false;
}
@ -630,7 +690,8 @@ int dp_display_request_irq(struct msm_dp *dp_display)
return rc;
}
rc = devm_request_irq(&dp->pdev->dev, dp->irq, dp_display_irq,
rc = devm_request_threaded_irq(&dp->pdev->dev, dp->irq,
dp_display_irq, dp_display_hpd_isr_work,
IRQF_TRIGGER_HIGH, "dp_display_isr", dp);
if (rc < 0) {
DRM_ERROR("failed to request IRQ%u: %d\n",
@ -829,6 +890,39 @@ void __exit msm_dp_unregister(void)
platform_driver_unregister(&dp_display_driver);
}
static void dp_display_config_hpd_work(struct work_struct *work)
{
struct dp_display_private *dp;
struct delayed_work *dw = to_delayed_work(work);
dp = container_of(dw, struct dp_display_private, config_hpd_work);
dp_display_host_init(dp);
dp_catalog_ctrl_hpd_config(dp->catalog);
/* set default to 0 */
atomic_set(&dp->hpd_isr_status, 0);
/* Enable interrupt first time
* we are leaving dp clocks on during disconnect
* and never disable interrupt
*/
enable_irq(dp->irq);
}
void msm_dp_irq_postinstall(struct msm_dp *dp_display)
{
struct dp_display_private *dp;
if (!dp_display)
return;
dp = container_of(dp_display, struct dp_display_private, dp_display);
INIT_DELAYED_WORK(&dp->config_hpd_work, dp_display_config_hpd_work);
queue_delayed_work(system_wq, &dp->config_hpd_work, HZ * 10);
}
int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
struct drm_encoder *encoder)
{

View File

@ -54,10 +54,22 @@
#define DP_DP_IRQ_HPD_INT_MASK (0x00000002)
#define DP_DP_HPD_REPLUG_INT_MASK (0x00000004)
#define DP_DP_HPD_UNPLUG_INT_MASK (0x00000008)
#define DP_DP_HPD_INT_MASK (DP_DP_HPD_PLUG_INT_MASK | \
DP_DP_IRQ_HPD_INT_MASK | \
DP_DP_HPD_REPLUG_INT_MASK | \
DP_DP_HPD_UNPLUG_INT_MASK)
#define DP_DP_HPD_STATE_STATUS_CONNECTED (0x40000000)
#define DP_DP_HPD_STATE_STATUS_PENDING (0x20000000)
#define DP_DP_HPD_STATE_STATUS_DISCONNECTED (0x00000000)
#define DP_DP_HPD_STATE_STATUS_MASK (0xE0000000)
#define REG_DP_DP_HPD_REFTIMER (0x00000018)
#define DP_DP_HPD_REFTIMER_ENABLE (1 << 16)
#define REG_DP_DP_HPD_EVENT_TIME_0 (0x0000001C)
#define REG_DP_DP_HPD_EVENT_TIME_1 (0x00000020)
#define DP_DP_HPD_EVENT_TIME_0_VAL (0x3E800FA)
#define DP_DP_HPD_EVENT_TIME_1_VAL (0x1F407D0)
#define REG_DP_AUX_CTRL (0x00000030)
#define DP_AUX_CTRL_ENABLE (0x00000001)

View File

@ -395,6 +395,7 @@ int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder);
void msm_dp_display_mode_set(struct msm_dp *dp, struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void msm_dp_irq_postinstall(struct msm_dp *dp_display);
#else
static inline int __init msm_dp_register(void)
@ -426,6 +427,11 @@ static inline void msm_dp_display_mode_set(struct msm_dp *dp,
struct drm_display_mode *adjusted_mode)
{
}
static inline void msm_dp_irq_postinstall(struct msm_dp *dp_display)
{
}
#endif
void __init msm_mdp_register(void);