lkml.org 
[lkml]   [2022]   [Aug]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v16 4/8] drm/mediatek: Add MT8195 External DisplayPort support
    Date
    From: Guillaume Ranquet <granquet@baylibre.com>

    This patch adds External DisplayPort support to the mt8195 eDP driver.

    Signed-off-by: Guillaume Ranquet <granquet@baylibre.com>
    Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com>
    Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
    ---
    drivers/gpu/drm/mediatek/mtk_dp.c | 190 +++++++++++++++++++++-----
    drivers/gpu/drm/mediatek/mtk_dp_reg.h | 4 +
    2 files changed, 158 insertions(+), 36 deletions(-)

    diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
    index 59fee814075b..00971ea2fadf 100644
    --- a/drivers/gpu/drm/mediatek/mtk_dp.c
    +++ b/drivers/gpu/drm/mediatek/mtk_dp.c
    @@ -77,6 +77,7 @@ struct mtk_dp {
    struct dp_cal_data cal_data;
    u8 max_lanes;
    u8 max_linkrate;
    + const struct mtk_dp_data *data;

    struct drm_device *drm_dev;
    struct drm_bridge bridge;
    @@ -96,6 +97,12 @@ struct mtk_dp {
    struct drm_connector *conn;
    };

    +struct mtk_dp_data {
    + int bridge_type;
    + unsigned int smp_cmd;
    + unsigned int cali_data_fmt;
    +};
    +
    static struct regmap_config mtk_dp_regmap_config = {
    .reg_bits = 32,
    .val_bits = 32,
    @@ -347,6 +354,14 @@ static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp)
    return mtk_dp->train_info.cable_plugged_in;
    }

    +static bool mtk_dp_plug_state_avoid_pulse(struct mtk_dp *mtk_dp)
    +{
    + bool ret;
    +
    + return !(readx_poll_timeout(mtk_dp_plug_state, mtk_dp, ret, ret,
    + 4000, 7 * 4000));
    +}
    +
    static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
    {
    mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640,
    @@ -784,35 +799,73 @@ static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
    return;
    }

    - cal_data->glb_bias_trim =
    - check_cal_data_valid(mtk_dp, 1, 0x1e,
    - (buf[3] >> 27) & 0x1f, 0xf);
    - cal_data->clktx_impse =
    - check_cal_data_valid(mtk_dp, 1, 0xe,
    - (buf[0] >> 9) & 0xf, 0x8);
    - cal_data->ln_tx_impsel_pmos[0] =
    - check_cal_data_valid(mtk_dp, 1, 0xe,
    - (buf[2] >> 28) & 0xf, 0x8);
    - cal_data->ln_tx_impsel_nmos[0] =
    - check_cal_data_valid(mtk_dp, 1, 0xe,
    - (buf[2] >> 24) & 0xf, 0x8);
    - cal_data->ln_tx_impsel_pmos[1] =
    - check_cal_data_valid(mtk_dp, 1, 0xe,
    - (buf[2] >> 20) & 0xf, 0x8);
    - cal_data->ln_tx_impsel_nmos[1] =
    - check_cal_data_valid(mtk_dp, 1, 0xe,
    - (buf[2] >> 16) & 0xf, 0x8);
    - cal_data->ln_tx_impsel_pmos[2] =
    - check_cal_data_valid(mtk_dp, 1, 0xe,
    - (buf[2] >> 12) & 0xf, 0x8);
    - cal_data->ln_tx_impsel_nmos[2] =
    - check_cal_data_valid(mtk_dp, 1, 0xe,
    - (buf[2] >> 8) & 0xf, 0x8);
    - cal_data->ln_tx_impsel_pmos[3] =
    - check_cal_data_valid(mtk_dp, 1, 0xe,
    - (buf[2] >> 4) & 0xf, 0x8);
    - cal_data->ln_tx_impsel_nmos[3] =
    - check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] & 0xf, 0x8);
    + /*
    + * To save the efuse bits, we place the calibration data for DP and eDP
    + * using method which could save the efuse bits. For this, the efuse
    + * orders of DP and eDP are different.
    + */
    +
    + if (mtk_dp->data->cali_data_fmt == MTK_DP_CALI_DATA_FMT_MT8195_EDP) {
    + cal_data->glb_bias_trim =
    + check_cal_data_valid(mtk_dp, 1, 0x1e,
    + (buf[3] >> 27) & 0x1f, 0xf);
    + cal_data->clktx_impse =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[0] >> 9) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_pmos[0] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[2] >> 28) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_nmos[0] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[2] >> 24) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_pmos[1] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[2] >> 20) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_nmos[1] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[2] >> 16) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_pmos[2] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[2] >> 12) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_nmos[2] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[2] >> 8) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_pmos[3] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[2] >> 4) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_nmos[3] =
    + check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] & 0xf, 0x8);
    + } else {
    + cal_data->glb_bias_trim =
    + check_cal_data_valid(mtk_dp, 1, 0x1e,
    + (buf[0] >> 27) & 0x1f, 0xf);
    + cal_data->clktx_impse =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[0] >> 13) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_pmos[0] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[1] >> 28) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_nmos[0] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[1] >> 24) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_pmos[1] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[1] >> 20) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_nmos[1] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[1] >> 16) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_pmos[2] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[1] >> 12) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_nmos[2] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[1] >> 8) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_pmos[3] =
    + check_cal_data_valid(mtk_dp, 1, 0xe,
    + (buf[1] >> 4) & 0xf, 0x8);
    + cal_data->ln_tx_impsel_nmos[3] =
    + check_cal_data_valid(mtk_dp, 1, 0xe, buf[1] & 0xf, 0x8);
    + }

    kfree(buf);
    }
    @@ -932,7 +985,7 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
    VIDEO_MUTE_SEL_DP_ENC0_P0_MASK |
    VIDEO_MUTE_SW_DP_ENC0_P0_MASK);

    - mtk_dp_sip_atf_call(mtk_dp, MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable);
    + mtk_dp_sip_atf_call(mtk_dp, mtk_dp->data->smp_cmd, enable);
    }

    static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
    @@ -1232,6 +1285,9 @@ static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
    drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
    usleep_range(2000, 5000);

    + if (!mtk_dp_plug_state(mtk_dp))
    + return -ENODEV;
    +
    drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);

    train_info->link_rate = min_t(int, mtk_dp->max_linkrate,
    @@ -1283,6 +1339,9 @@ static int mtk_dp_train_start(struct mtk_dp *mtk_dp)
    u8 train_limit;
    u8 max_link_rate;

    + if (!mtk_dp_plug_state_avoid_pulse(mtk_dp))
    + return -ENODEV;
    +
    link_rate = mtk_dp->rx_cap[1];
    lane_count = mtk_dp->rx_cap[2] & 0x1F;

    @@ -1457,9 +1516,20 @@ static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
    else
    train_info->cable_plugged_in = false;

    - mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
    - DP_PWR_STATE_BANDGAP_TPLL_LANE,
    - DP_PWR_STATE_MASK);
    + if (!train_info->cable_plugged_in) {
    + mtk_dp_video_mute(mtk_dp, true);
    +
    + mtk_dp_initialize_priv_data(mtk_dp);
    + mtk_dp_set_idle_pattern(mtk_dp, true);
    +
    + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
    + DP_PWR_STATE_BANDGAP_TPLL,
    + DP_PWR_STATE_MASK);
    + } else {
    + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
    + DP_PWR_STATE_BANDGAP_TPLL_LANE,
    + DP_PWR_STATE_MASK);
    + }

    return IRQ_HANDLED;
    }
    @@ -1503,6 +1573,21 @@ static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
    return 0;
    }

    +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
    +{
    + struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
    + enum drm_connector_status ret = connector_status_disconnected;
    + u8 sink_count = 0;
    +
    + if (mtk_dp_plug_state_avoid_pulse(mtk_dp)) {
    + drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
    + if (DP_GET_SINK_COUNT(sink_count))
    + ret = connector_status_connected;
    + }
    +
    + return ret;
    +}
    +
    static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
    struct drm_connector *connector)
    {
    @@ -1857,6 +1942,7 @@ static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
    .atomic_disable = mtk_dp_bridge_atomic_disable,
    .mode_valid = mtk_dp_bridge_mode_valid,
    .get_edid = mtk_dp_get_edid,
    + .detect = mtk_dp_bdg_detect,
    };

    static int mtk_dp_probe(struct platform_device *pdev)
    @@ -1871,6 +1957,7 @@ static int mtk_dp_probe(struct platform_device *pdev)
    return -ENOMEM;

    mtk_dp->dev = dev;
    + mtk_dp->data = (struct mtk_dp_data *)of_device_get_match_data(dev);

    irq_num = platform_get_irq(pdev, 0);
    if (irq_num < 0)
    @@ -1878,9 +1965,15 @@ static int mtk_dp_probe(struct platform_device *pdev)
    "failed to request dp irq resource\n");

    mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
    - if (IS_ERR(mtk_dp->next_bridge))
    + if (IS_ERR(mtk_dp->next_bridge) &&
    + PTR_ERR(mtk_dp->next_bridge) == -ENODEV) {
    + dev_info(dev,
    + "No panel connected in devicetree, continue as external DP\n");
    + mtk_dp->next_bridge = NULL;
    + } else if (IS_ERR(mtk_dp->next_bridge)) {
    return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
    "Failed to get bridge\n");
    + }

    ret = mtk_dp_dt_parse(mtk_dp, pdev);
    if (ret)
    @@ -1923,7 +2016,7 @@ static int mtk_dp_probe(struct platform_device *pdev)

    mtk_dp->bridge.ops =
    DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
    - mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP;
    + mtk_dp->bridge.type = mtk_dp->data->bridge_type;

    drm_bridge_add(&mtk_dp->bridge);

    @@ -1950,6 +2043,12 @@ static int mtk_dp_suspend(struct device *dev)
    {
    struct mtk_dp *mtk_dp = dev_get_drvdata(dev);

    + if (mtk_dp_plug_state(mtk_dp)) {
    + drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
    + /* Ensure the sink is off before shutting down power */
    + usleep_range(2000, 3000);
    + }
    +
    mtk_dp_power_disable(mtk_dp);

    mtk_dp_hwirq_enable(mtk_dp, false);
    @@ -1981,8 +2080,27 @@ static int mtk_dp_resume(struct device *dev)

    static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);

    +static const struct mtk_dp_data mt8195_edp_data = {
    + .bridge_type = DRM_MODE_CONNECTOR_eDP,
    + .smp_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
    + .cali_data_fmt = MTK_DP_CALI_DATA_FMT_MT8195_EDP,
    +};
    +
    +static const struct mtk_dp_data mt8195_dp_data = {
    + .bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
    + .smp_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
    + .cali_data_fmt = MTK_DP_CALI_DATA_FMT_MT8195_DP,
    +};
    +
    static const struct of_device_id mtk_dp_of_match[] = {
    - { .compatible = "mediatek,mt8195-edp-tx" },
    + {
    + .compatible = "mediatek,mt8195-edp-tx",
    + .data = &mt8195_edp_data,
    + },
    + {
    + .compatible = "mediatek,mt8195-dp-tx",
    + .data = &mt8195_dp_data,
    + },
    {},
    };
    MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
    diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
    index 3676d71bd816..c12742adaa3c 100644
    --- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
    +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
    @@ -14,6 +14,10 @@
    #define SEC_OFFSET 0x4000

    #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE (BIT(0) | BIT(5))
    +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE BIT(5)
    +
    +#define MTK_DP_CALI_DATA_FMT_MT8195_EDP 0
    +#define MTK_DP_CALI_DATA_FMT_MT8195_DP 1

    #define DP_PHY_GLB_BIAS_GEN_00 0
    #define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(20, 16)
    --
    2.18.0
    \
     
     \ /
      Last update: 2022-08-05 12:18    [W:5.857 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site