mirror of https://gitee.com/openkylin/linux.git
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:
parent
a10476e450
commit
220b856a3d
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
if (en) {
|
||||
u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);
|
||||
u32 config = dp_read_aux(catalog, REG_DP_DP_HPD_INT_MASK);
|
||||
|
||||
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);
|
||||
config = (en ? config | intr_mask : config & ~intr_mask);
|
||||
|
||||
/* Configure REFTIMER */
|
||||
reftimer |= REFTIMER_DEFAULT_VALUE;
|
||||
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_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);
|
||||
|
||||
u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);
|
||||
|
||||
/* 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 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);
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue